Multiple Models in a single django ModelForm? Multiple Models in a single django ModelForm? python python

Multiple Models in a single django ModelForm?


You can just show both forms in the template inside of one <form> html element. Then just process the forms separately in the view. You'll still be able to use form.save() and not have to process db loading and saving yourself.

In this case you shouldn't need it, but if you're going to be using forms with the same field names, look into the prefix kwarg for django forms. (I answered a question about it here).


You can try to use this pieces of code:

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

Example Usage:

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())


erikbwork and me both had the problem that one can only include one model into a generic Class Based View. I found a similar way of approaching it like Miao, but more modular.

I wrote a Mixin so you can use all generic Class Based Views. Define model, fields and now also child_model and child_field - and then you can wrap fields of both models in a tag like Zach describes.

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())

Example Usage:

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

Or with ModelFormClass:

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

Done. Hope that helps someone.