Несколько моделей в одной django ModelForm? - PullRequest
85 голосов
/ 05 мая 2010

Можно ли включить несколько моделей в один ModelForm в django? Я пытаюсь создать форму для редактирования профиля. Поэтому мне нужно включить некоторые поля из модели User и модели UserProfile. В настоящее время я использую 2 формы, подобные этой

class UserEditForm(ModelForm):

    class Meta:
        model = User
        fields = ("first_name", "last_name")

class UserProfileForm(ModelForm):

    class Meta:
        model = UserProfile
        fields = ("middle_name", "home_phone", "work_phone", "cell_phone")

Есть ли способ объединить их в одну форму или мне просто нужно создать форму и обработать загрузку и сохранение БД?

Ответы [ 5 ]

82 голосов
/ 05 мая 2010

Вы можете просто показать обе формы в шаблоне внутри одного <form> html-элемента. Затем просто обработайте формы отдельно в представлении. Вы по-прежнему сможете использовать form.save() и вам не придется обрабатывать загрузку и сохранение БД.

В этом случае вам это не нужно, но если вы собираетесь использовать формы с одинаковыми именами полей, посмотрите на prefix kwarg для форм django. (Я ответил на вопрос об этом здесь ).

8 голосов
/ 22 июня 2014

Вы можете попробовать использовать следующие части кода:

class CombinedFormBase(forms.Form):
    form_classes = []

    def __init__(self, *args, **kwargs):
        super(CombinedFormBase, self).__init__(*args, **kwargs)
        for f in self.form_classes:
            name = f.__name__.lower()
            setattr(self, name, f(*args, **kwargs))
            form = getattr(self, name)
            self.fields.update(form.fields)
            self.initial.update(form.initial)

    def is_valid(self):
        isValid = True
        for f in self.form_classes:
            name = f.__name__.lower()
            form = getattr(self, name)
            if not form.is_valid():
                isValid = False
        # is_valid will trigger clean method
        # so it should be called after all other forms is_valid are called
        # otherwise clean_data will be empty
        if not super(CombinedFormBase, self).is_valid() :
            isValid = False
        for f in self.form_classes:
            name = f.__name__.lower()
            form = getattr(self, name)
            self.errors.update(form.errors)
        return isValid

    def clean(self):
        cleaned_data = super(CombinedFormBase, self).clean()
        for f in self.form_classes:
            name = f.__name__.lower()
            form = getattr(self, name)
            cleaned_data.update(form.cleaned_data)
        return cleaned_data

Пример использования:

class ConsumerRegistrationForm(CombinedFormBase):
    form_classes = [RegistrationForm, ConsumerProfileForm]

class RegisterView(FormView):
    template_name = "register.html"
    form_class = ConsumerRegistrationForm

    def form_valid(self, form):
        # some actions...
        return redirect(self.get_success_url())
3 голосов
/ 10 января 2017

У erikbwork и у меня обоих была проблема, что в общее представление на основе классов можно включить только одну модель. Я нашел такой же подход, как Мяо, но более модульный.

Я написал Mixin, чтобы вы могли использовать все общие представления на основе классов. Определите модель, поля и теперь также child_model и child_field - и тогда вы можете заключить поля обеих моделей в тег, как описывает Зак.

class ChildModelFormMixin: 
    ''' extends ModelFormMixin with the ability to include ChildModelForm '''
    child_model = ""
    child_fields = ()
    child_form_class = None

    def get_child_model(self):
        return self.child_model

    def get_child_fields(self):
        return self.child_fields

    def get_child_form(self):
        if not self.child_form_class:
            self.child_form_class = model_forms.modelform_factory(self.get_child_model(), fields=self.get_child_fields())
        return self.child_form_class(**self.get_form_kwargs())

    def get_context_data(self, **kwargs):
        if 'child_form' not in kwargs:
            kwargs['child_form'] = self.get_child_form()
        return super().get_context_data(**kwargs)

    def post(self, request, *args, **kwargs):
        form = self.get_form()
        child_form = self.get_child_form()

        # check if both forms are valid
        form_valid = form.is_valid()
        child_form_valid = child_form.is_valid()

        if form_valid and child_form_valid:
            return self.form_valid(form, child_form)
        else:
            return self.form_invalid(form)

    def form_valid(self, form, child_form):
        self.object = form.save()
        save_child_form = child_form.save(commit=False)
        save_child_form.course_key = self.object
        save_child_form.save()

        return HttpResponseRedirect(self.get_success_url())

Пример использования:

class ConsumerRegistrationUpdateView(UpdateView):
    model = Registration
    fields = ('firstname', 'lastname',)
    child_model = ConsumerProfile
    child_fields = ('payment_token', 'cart',)

Или с ModelFormClass:

class ConsumerRegistrationUpdateView(UpdateView):
    model = Registration
    fields = ('firstname', 'lastname',)
    child_model = ConsumerProfile
    child_form_class = ConsumerProfileForm

Готово. Надеюсь, это кому-нибудь поможет.

2 голосов
/ 28 ноября 2011

Вы можете проверить мой ответ здесь для аналогичной проблемы.

В нем рассказывается о том, как объединить регистрацию и профиль пользователя в одну форму, но его можно обобщить для любой комбинации ModelForm.

2 голосов
/ 05 мая 2010

Вам, вероятно, стоит взглянуть на Встроенные формы . Встроенные формы используются, когда ваши модели связаны внешним ключом.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...