Filly

[HOW TO] Paginate a Django formset

Last month I needed to paginate a Django model formset and I couldn’t find the way to do it, but I found posts asking the same question without an answer, so here is my solution:

In the forms.py file:

class MyForm(ModelForm):  
    class Meta:  
        model = MyModel  
        fields = ('description',)

In the views.py file:

FormSet = modelformset_factory(MyModel, form=MyForm, extra=0)  
if request.method == 'POST':  
    formset = FormSet(request.POST, request.FILES)  
    if formset.is_valid():  
        formset.save()  
    return HttpResponse('Formset submited!')  
else:  
    query = MyModel.objects.filter(condition)  
    paginator = Paginator(query, 10)  # Show 10 forms per page  
    page = request.GET.get('page')  
    try:  
        objects = paginator.page(page)  
    except PageNotAnInteger:  
        objects = paginator.page(1)  
    except EmptyPage:  
        objects = paginator.page(paginator.num_pages)  
    page_query = MyModel.objects.filter(id__in=[object.id for object in objects])  
    formset = FormSet(queryset=page_query)  
    context = {'objects': objects, 'formset': formset}  
    return render_to_response('template.html', context,  
                              context_instance=RequestContext(request))

You need to create the formset with the objects in the present page, otherwise, when you try to do formset = FormSet(request.POST, request.FILES) in the POST method, Django raises a MultiValueDictKeyError error.

In the template.html file:

{% extends "base.html" %}
{% block content %}
<h1>Paginated formset</h1>
{% if objects %}
    <form action="" method="post">
        {% csrf_token %}
        {{ formset.management_form }}
        {% for form in formset.forms %}
            {{ form.id }}
            <div class="object-frame">
                <div class="object">
                    <p><span>Name: </span>{{ form.instance.name }}</p>
                    {{ form.as_p }}
                </div>
            </div>
        {% endfor %}
        <input value="Save" type="submit">
    </form>
 
    <div class="pagination">
        <span class="step-links">
            {% if objects.has_previous %}
                <a href="?page={{ objects.previous_page_number }}">Previous</a>
            {% endif %}
 
            <span class="current">
                Page {{ objects.number }} of {{ objects.paginator.num_pages }}
            </span>
 
            {% if objects.has_next %}
                <a href="?page={{ objects.next_page_number }}">next</a>
            {% endif %}
        </span>
    </div>
{% else %}
    <p>There are no objects.</p>
{% endif %}
{% endblock %}