Django inlineformsetfactory - What is it good for?

Mike picture Mike · Dec 16, 2009 · Viewed 7.5k times · Source

Sorry for a newbie question but...

Can someone shed some light on what is the use case for inlineformset_factory?

I have followed example from Django documentation:

#Models
class Author(models.Model):
    name = models.CharField(max_length=100)

class Book(models.Model):
    author = models.ForeignKey(Author)
    title = models.CharField(max_length=100)

#View

def jojo(request):

    BookFormSet = inlineformset_factory(Author, Book)
    author = Author.objects.get(name=u'Mike Royko')
    formset = BookFormSet(instance=author)


    return render_to_response('jojo.html', {
        'formset': formset,
    })

#jojo.html
<form action="" method="POST">
<table>

{{ formset }}

</table>
<input type="submit" value="Submit" />
</form>

But it only displays book fields.

My understanding was that formset would display Book form with inline Author form just like Django Admin. On top of that I can't easily pass initial values to formset?

Then how is it better then using two separate AuthorForm and BookForm?

Or am i missing something obvious?

Answer

Jay H. picture Jay H. · Mar 28, 2012

inlineformset_factory only provides multiple forms for the nested elements, you need a separate form at the top if you want a form for the main model.

Here is an example of a working inlineformset_factory with the main form embedded at the top:

views.py

from django.shortcuts import get_object_or_404, render_to_response
from django.forms.models import inlineformset_factory
from django.http import HttpResponseRedirect
from django.template import RequestContext

from App_name.models import * #E.g. Main, Nested, MainForm, etc.

. . .

@login_required
def Some_view(request, main_id=None, redirect_notice=None):
    #login stuff . . .
    c = {}
    c.update(csrf(request))
    c.update({'redirect_notice':redirect_notice})#Redirect notice is an optional argument I use to send user certain notifications, unrelated to this inlineformset_factory example, but useful.

    #Intialization --- The start of the view specific functions
    NestedFormset = inlineformset_factory(Main, Nested, can_delete=False, )
    main = None
    if main_id :
        main = Main.objects.get(id=main_id)#get_object_or_404 is also an option

    # Save new/edited Forms
    if request.method == 'POST':
        main_form = MainForm(request.POST, instance=main, prefix='mains')
        formset = NestedFormset(request.POST, request.FILES, instance=main, prefix='nesteds')
        if main_form.is_valid() and formset.is_valid():
            r = main_form.save(commit=False)
            #do stuff, e.g. setting any values excluded in the MainForm
            formset.save()
            r.save()
            return HttpResponseRedirect('/Home_url/')
    else:
        main_form = MainForm(instance=main, prefix='mains') #initial can be used in the MainForm here like normal.
        formset = NestedFormset(instance=main, prefix='nesteds')
    c.update({'main_form':main_form, 'formset':formset, 'realm':realm, 'main_id':main_id})
    return render_to_response('App_name/Main_nesteds.html', c, context_instance=RequestContext(request))

template.html

{% if main_form %}
<form action="." method="POST">{% csrf_token %}
    {{ formset.management_form }}
    <table>
        {{main_form.as_table}}
        {% for form in formset.forms %}
            <table>{{ form }}</table>
        {% endfor %}
    </table>
    <p><input type="submit" name="submit" value="Submit" class="button"></p>
</form>
{% endif %}