Multiple models in one Django ModelForm model?

Is it possible to have multiple models included in one ModelForm in django? I am trying to create a profile edit form. Therefore, I need to include some fields in the User Model and UserProfile model. I am currently using 2 forms such as

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

Is there a way to combine them into one form or do I just need to create a form and handle the db loading and save myself?

+71
python django django-forms
May 05 '10 at 5:23 a.m.
source share
5 answers

You can simply show both forms in a template inside one html <form> element. Then just process the forms separately in the view. You can still use form.save() and shouldn't handle db loading and save yourself.

You don’t need it in this case, but if you intend to use forms with the same field names, check out prefix kwarg for django forms. (I answered the question about this here ).

+69
May 05 '10 at 3:45 p.m.
source share

You can try using these code snippets:

 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 

Usage example:

 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()) 
+5
Jun 22 '14 at 7:54
source share

You should probably take a look at the inline forms . Embedded forms are used when your models are associated with a foreign key.

+2
May 05 '10 at 5:43 a.m.
source share

You can check my answer here for a similar problem.

It talks about how to combine the registration and user profile into one form, but it can be generalized to any combination of ModelForm.

+2
Nov 28 '11 at 0:10
source share

erikbwork, and I had a problem that you can include only one model in the general cool approach. I found a similar approach to it, like Miao, but more modular.

I wrote Mixin so you can use all the general representations of the class. Define the model, fields and now also child_model and child_field - and then you can wrap the fields of both models in a tag, as described by Zach.

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

Usage example:

 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.

+2
Jan 09 '17 at 23:57
source share



All Articles