Model Formset in Django

Model Formset in Django

Django Model Formsets

Introduction

Formset is a way of working with multiple forms at the same time. Using Formset you can use multiple forms on a single page. In this article, we will be creating formset using a model and see all the options that are available to customize formset.

Creating Formset

To create a formset we use the modelformset_factory function. You pass the Model and fields for which you want to make the Formset. modelformset_factory returns the Formset for the given model with the provided fields. Therefore it will be something like this:

from django.forms import modelformset_factory,ModelForm
from django.db import models

# Note Model Definition
class Note(models.Model):
    title = models.CharField(max_length=80)
    description = models.TextField()


# Here we create a formset of Note
NoteFormset = formset_factory(Note, fields=['title','description'])

While creating a formset you can pass an additional parameter extra with a number then the Formset will contain that many empty forms.

If you have created a ModelForm with custom fields you can pass it to modelformset_factory

#Example 
#Creating formset from NoteForm For Note with 5 empty forms
NoteFormset = formset_factory(Note,NoteForm, extra=5)
# This will create 5 empty forms in the formset

Formset with initial data

You can provide initial data for forms by passing a list of dictionaries with data. This will create forms equal to the length of initial data provided(Total number of dictionaries in the initial data list) + the value of the extra parameter passed during the creation of Formset. If you want to fill data from the database you can pass queryset as keyword argument and the forms will be filled with the data from the passed queryset.

Look at this code for better clarity.

NoteFormset = formset_factory(NoteForm,extra=5)
# This formet contains 5 empty forms
# This list contain 3 dictionaries
data = [
        {'title':"note no-1","description":"description-1"},
        {'title':"note no-2","description":"description-2"},
        {'title':"note no-3","description":"description-3"}
        ]
# We passed the initial data to the NoteFormset
# Now forms contain 3 forms with data and 5 empty forms
# So total 8 
# filling the form with initial data
forms = NoteFormset(initial=data)

# Passing data from database
# for filling formset with queryset
forms = NoteFormset(queryset=Note.objects.all())
# for keeping the formset empty
forms = NoteFormset(queryset=Note.objects.none())

In this way, you can provide data to be filled in the forms of the formset.

Controlling the number of forms in a Formset

You can also control the number of forms to be displayed. For this, you need to pass another parameter called max_num to the Formset. Have a look at the following snippet.

# This list contain 3 dictionaries
data = [
        {'title':"note no-1","description":"description-1"},
        {'title':"note no-2","description":"description-2"},
        {'title':"note no-3","description":"description-3"}
        ]

formset = NoteFormset(initial=data, extra=4, max_num=5)
# This form will display 3 forms filled with data and 2 empty forms
# Keep this in mind if the number of forms with data exceeds the max_num
# then also all the forms with the data will be created but 0 empty forms

Even if you provide the max_num less than forms with data then all forms with data + empty forms till max_num will be created.

Another parameter absolute_max can be passed to formset_factory to set the limit for the number of forms to create from POST data or initial data. In this way even if the initial data exceeds the absolute_max still only absolute_max number of forms will be instantiated.

NoteFormset = formset_factory(Note, NoteForm, absolute_max=30)
# Now NoteFormset cannot contain forms more than absolute_max value

Note: Absolute_max should be greater than max_num else ValueError will be raised

Passing the POST data to a formset

To pass POST data you simply do this

If your form also deals with files/images don't forget to pass request.FILES to the formset while processing formset

formset = NoteFormset(request.POST, request.FILES)
if formset.is_valid():
    formset.save()

Formset validation

To validate a formset call is_valid(). This will validate if the data entered in the forms was valid or not. is_valid() method returns a boolean denoting whether formset is valid or not.

Rendering Formset in templates

Now, we will be looking at how to render formset in a template

# View function
def process_form(request):
    if request.method=='POST':
        # processing formset
        return redirect('next view')
    data = Note.objects.all()
    # If we do not provide any argument to the formset then
    # also it will be filled with queryset of the model
    formset = NoteFormset()
    return render(request,'template/form.html',context={'formset',formset})

Let's look at form.html

Note: If you are dealing with files/images in the form don't miss enctype="multipart/form-data"

<form method="post" enctype="multipart/form-data">
    {% csrf_token %}
    {{ formset.management_form }}
    {% for form in formset %}
        {{ form.as_p }}
    {% endfor %}
    <input type="submit" value="Save">
</form>

It is important to include a management form inside the HTML form. So you will add {{formset.management_form}}

And this is all that you need to know to start using model formsets. For learning more about formset read the official docs here Formset and ModelFormset.

So are still reading why don't you like this post and share it will your fellow pythonistas and learners?

Share your views and story of learning tech.

Did you find this article valuable?

Support Rishabh Kumar Bahukhandi by becoming a sponsor. Any amount is appreciated!