Django class based views with inline model or form

I have the following models:

class Bill(models.Model): date = models.DateTimeField(_("Date of bill"),null=True,blank=True) class Item(models.Model): name = models.CharField(_("Name"),max_length=100) price = models.FloatField(_("Price")) quantity = models.IntegerField(_("Quantity")) bill = models.ForeignKey("Bill",verbose_name=_("Bill"), related_name="billitem") 

I know this is possible:

 from django.forms.models import inlineformset_factory inlineformset_factory(Bill, Item) 

and then process this through the standard view.

Now I was wondering if there is a way to achieve the same (which means: using the built-in to add / edit elements belonging to the account) using class-based views (not for the admin interface).

+48
django inline-formset django-class-based-views
Dec 21 '10 at 9:07
source share
7 answers

Key points:

  • generated by FormSet inside forms.py using inlineformset_factory :

     BookImageFormSet = inlineformset_factory(BookForm, BookImage, extra=2) BookPageFormSet = inlineformset_factory(BookForm, BookPage, extra=5) 
  • returned a FormSet in the CreateView class in views.py :

     def get_context_data(self, **kwargs): context = super(BookCreateView, self).get_context_data(**kwargs) if self.request.POST: context['bookimage_form'] = BookImageFormSet(self.request.POST) context['bookpage_form'] = BookPageFormSet(self.request.POST) else: context['bookimage_form'] = BookImageFormSet() context['bookpage_form'] = BookPageFormSet() return context 
  • Used by form_valid to save the form and set of forms:

      def form_valid(self, form): context = self.get_context_data() bookimage_form = context['bookimage_formset'] bookpage_form = context['bookpage_formset'] if bookimage_form.is_valid() and bookpage_form.is_valid(): self.object = form.save() bookimage_form.instance = self.object bookimage_form.save() bookpage_form.instance = self.object bookpage_form.save() return HttpResponseRedirect('thanks/') else: return self.render_to_response(self.get_context_data(form=form)) 
+53
Jul 25 '11 at 18:19
source share
— -

I just added my own version after checking out some of these pre-prepared CBVs. I specifically needed control over multiple formsets -> one parent in the same view with individual save functions.

I basically filled out the FormSet data binding to the get_named_formsets function, which is called get_context_data and form_valid .

There I check to see if all forms are valid, and also look for a method that overrides the plain old formset.save() based on each form set for user-defined savings.

The template displays forms using

 {% with named_formsets.my_specific_formset as formset %} {{ formset }} {{ formset.management_form }} {% endwith %} 

I think I will use this system regularly.

 class MyView(UpdateView): # FormView, CreateView, etc def get_context_data(self, **kwargs): ctx = super(MyView, self).get_context_data(**kwargs) ctx['named_formsets'] = self.get_named_formsets() return ctx def get_named_formsets(self): return { 'followup': FollowUpFormSet(self.request.POST or None, prefix='followup'), 'action': ActionFormSet(self.request.POST or None, prefix='action'), } def form_valid(self, form): named_formsets = self.get_named_formsets() if not all((x.is_valid() for x in named_formsets.values())): return self.render_to_response(self.get_context_data(form=form)) self.object = form.save() # for every formset, attempt to find a specific formset save function # otherwise, just save. for name, formset in named_formsets.items(): formset_save_func = getattr(self, 'formset_{0}_valid'.format(name), None) if formset_save_func is not None: formset_save_func(formset) else: formset.save() return http.HttpResponseRedirect('') def formset_followup_valid(self, formset): """ Hook for custom formset saving.. useful if you have multiple formsets """ followups = formset.save(commit=False) # self.save_formset(formset, contact) for followup in followups: followup.who = self.request.user followup.contact = self.object followup.save() 
+12
Aug 10 '12 at 23:17
source share

You should try django-extra-views . Find CreateWithInlinesView and UpdateWithInlinesView .

+7
Nov 03 '13 at 19:38
source share

I edge the generic source code 1.3-beta-1:

The code is absolutely not ready to edit the list or there is black magic here. But I think that it can be quickly implemented.

If you look at the django.view.generic.edit module (which supports detailed editing of objects), how it uses the django.view.generic.detail module.

I think the django.view.generic.list_edit module can be implemented using django.view.generic.list and some part from django.view.generic.edit.

+1
Jan 24 '11 at 11:32
source share

I made some modification of the original solution to allow formet.is_valid () to work:

  if self.request.POST: context['fs'] = MyInlineFS(self.request.POST, instance=self.object) else: context['fs'] = MyInlineFS(instance=self.object) 
+1
Oct 22 2018-11-11T00:
source share

The code in Jordan's answer did not work for me. I posted my own question about this, which, I believe, has now been resolved. The first argument to inlineformset_factory should be Book, not BookForm.

+1
Jun 25 2018-12-12T00:
source share

I needed to make one more modification to represent Jordan and Speq get_context_data() to have formset.non_form_errors in the template context.

 ... if self.request.POST: context['fs'] = MyInlineFS(self.request.POST, instance=self.object) context['fs'].full_clean() # <-- new else: context['fs'] = MyInlineFS(instance=self.object) return context 
+1
Jan 30 '13 at 16:10
source share



All Articles