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.