Django Formsets: How to Handle Multiple Forms in Django

March 2, 2023


0
7 min read
502

Django formsets allow developers to handle multiple forms in a single request. This can be useful when you want to create, update, or delete multiple objects at once. In this article, we will discuss how to use formsets in Django, and provide some examples to help you get started.

Before we dive into the details, let's first define what a formset is. A formset is a collection of forms that share the same structure and can be processed together. Each form in the formset represents an instance of a model, and the formset as a whole is used to handle multiple instances of that model.

Creating a Formset

To create a formset in Django, you first need to define a form that represents a single instance of your model. For example, if you have a model called Person, you might define a form like this:

from django import forms
from .models import Person

class PersonForm(forms.ModelForm):
    class Meta:
        model = Person
        fields = ['first_name', 'last_name', 'email']

Next, you need to import the formset_factory function from the django.forms module:

from django.forms import formset_factory

Then, you can create a formset by calling the formset_factory function, passing in the form you just defined and the number of forms you want to create:

PersonFormSet = formset_factory(PersonForm, extra=2)

In this example, we've created a formset called PersonFormSet that contains two forms.

Displaying a Formset

To display a formset in a template, you can use the formset variable that is automatically created by Django when you pass the formset to the context:

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

In this example, we've used a for loop to iterate over each form in the formset and display it using the as_p method, which renders the form as a series of paragraphs.

The management_form variable is a special variable that is required by Django to handle formsets correctly. It contains hidden fields that Django uses to keep track of the forms in the formset.

Processing a Formset

To process a formset in a view, you need to check whether the request is a GET or a POST request. If it's a GET request, you simply create a new instance of the formset and pass it to the template. If it's a POST request, you need to validate the formset and save the data if it's valid.

Here's an example view that processes a formset:

from django.shortcuts import render
from .forms import PersonFormSet

def my_view(request):
    if request.method == 'POST':
        formset = PersonFormSet(request.POST)
        if formset.is_valid():
            formset.save()
    else:
        formset = PersonFormSet()
    return render(request, 'my_template.html', {'formset': formset})

In this example, we've created a view called my_view that checks whether the request is a GET or a POST request. If it's a GET request, we create a new instance of the formset and pass it to the template. If it's a POST request, we validate the formset using the is_valid method and save the data using the save method if the formset is valid.

Deleting Objects with a Formset

In addition to creating and updating objects, formsets can also be used to delete objects. To do this, you need to add a BooleanField to the form that represents each object, and set its widget to forms.HiddenInput to hide it from the user.

Here's an example:

class PersonForm(forms.ModelForm):
    delete = forms.BooleanField(required=False, widget=forms.HiddenInput)

    class Meta:
        model = Person
        fields = ['first_name', 'last_name', 'email', 'delete']

In this example, we've added a BooleanField called delete to the PersonForm class. We've set its required attribute to False so that it doesn't need to be filled out, and its widget attribute to forms.HiddenInput so that it's hidden from the user.

To delete objects with a formset, you need to loop over the forms in the formset and delete any objects that have the delete field set to True. Here's an example view that does this:

from django.shortcuts import render
from .forms import PersonFormSet

def my_view(request):
    if request.method == 'POST':
        formset = PersonFormSet(request.POST)
        if formset.is_valid():
            for form in formset:
                if form.cleaned_data.get('delete'):
                    form.instance.delete()
                else:
                    form.save()
    else:
        formset = PersonFormSet()
    return render(request, 'my_template.html', {'formset': formset})

In this example, we've added a loop that iterates over the forms in the formset. For each form, we check whether the delete field is set to True using the cleaned_data attribute. If it is, we delete the corresponding object using the delete method. If it's not, we save the data using the save method as before.

Using Inline Formsets

In some cases, you may want to use a formset to edit related objects. For example, if you have a model called Person and a related model called PhoneNumber, you might want to use a formset to edit the phone numbers for each person.

To do this, you can use an inline formset. An inline formset is a formset that is embedded in a parent form, and is used to edit related objects. To create an inline formset, you need to use the inlineformset_factory function instead of the formset_factory function.

Here's an example:

from django.forms import inlineformset_factory
from .models import Person, PhoneNumber

PhoneNumberFormSet = inlineformset_factory(
    Person, PhoneNumber,
    fields=['number'],
    extra=1, can_delete=True
)

In this example, we've created an inline formset called PhoneNumberFormSet that is used to edit the phone numbers for each person. We've passed in the Person model and the PhoneNumber model as arguments, and specified the fields that we want to include in the formset using the fields argument.

We've also specified that the formset should include one extra form using the extra argument, and that it should allow objects to be deleted using the can_delete argument.

To display an inline formset in a template, you can use the formset variable as before, but you also need to include the parent form in the template. Here's an example:

<form method="post">
    {% csrf_token %}
    {{ form.as_p }}
    {{formset.management_form }}
    {% for form in formset %}
       {{ form.as_p }}
       {{ formset.empty_form.as_p }}
    {% endfor %}
    <button type="submit">Save</button>
</form>

In this example, we've included the parent form using the {{ form.as_p }} template tag, and the inline formset using a loop that iterates over the forms in the formset. We've also included the formset.management_form template tag to handle the management of the formset.

To process the form data in the view, you need to pass both the parent form and the formset to the view, and use the save method on both. Here's an example:

from django.shortcuts import render
from .forms import PersonForm
from .models import Person, PhoneNumber
from .forms import PhoneNumberFormSet

def my_view(request):
    person = Person.objects.get(id=1)
    if request.method == 'POST':
        form = PersonForm(request.POST, instance=person)
        formset = PhoneNumberFormSet(request.POST, instance=person)
        if form.is_valid() and formset.is_valid():
            form.save()
            formset.save()
    else:
        form = PersonForm(instance=person)
        formset = PhoneNumberFormSet(instance=person)
    return render(request, 'my_template.html', {'form': form, 'formset': formset})

In this example, we've passed both the parent form and the formset to the view, and used the instance argument to specify the object that we want to edit. We've also used the is_valid method to validate both forms, and the save method to save both forms.

Conclusion

Django formsets provide a powerful way to handle multiple forms in Django. They allow you to create, update, and delete multiple objects at once, and can be used to edit related objects using inline formsets. By understanding how to use formsets, you can save time and reduce the amount of code needed to handle complex form workflows.

django Formset Multiple-Forms Form Appreciate you stopping by my post! 😊

Add a comment


Note: If you use these tags, write your text inside the HTML tag.
Login Required
Author's profile
Profile Image

Abdulla Fajal

Django Developer

With 'espere.in' under my care, I, Abdulla Fajal, graciously invite your insights and suggestions, as we endeavour to craft an exquisite online experience together.