Fork me on GitHub

Some views do not work with one form, but with multiple forms. In that case it is important to work with prefixes such that the two forms will each process their part of the composite form.

Why is it a problem?

For example one can make two forms to fill in data about the father and the mother with:

from django import forms

class FatherForm(forms.Form):
    name = forms.CharField()
    first_name = forms.CharField()

class MotherForm(forms.Form):
    name = forms.CharField()
    first_name = forms.CharField()

The view then might be defined as:

from django.shortcuts import redirect, render

def some_view(request):
    if request.method == 'POST':
        father_form = FatherForm(request.POST)
        mother_form = MotherForm(request.POST)
        if father_form.is_valid() and mother_form.is_valid():
            # process the data …
            return redirect('name-of-some-view')
    else:
        father_form = FatherForm()
        mother_form = MotherForm()
    return render(
        request,
        'name-of-some-template.html',
        {'father_form': father_form, 'mother_form': mother_form}
    )

The problem is that if we process data with this view, the request will post two values for name and two values for first_name. The logic of the QueryDict [Django-doc] means that if we want to get the value for name and/or first_name; it will use the last value that was defined. This thus means that if we render the forms with:

<form method="POST" action="{% url 'name-of-some-view' %}">
    {% csrf_token %}
    {{ father_form }}
    {{ mother_form }}
</form>

both the father_form and the mother_form will use data from the form elements of the form of the mother. This is because the request will simply send two values for the name and the first_name item, and the two forms, will both use the last value defined.

What can be done to resolve the problem?

Django has a solution for this: it can prefix the name of the form elements with a prefix=… parameter [Django-doc]. This prefix parameter will add a prefix to all the form input items that arise from that form. For example if we specify prefix='father' for the FatherForm, then the name of the items will be father-name, and father-first_name. This thus means that Django now can process these as part of the FatherForm, and the others as part of the MotherForm.

Strictly speaking, for n forms, we can decide to only give n-1 a prefix and this will work, but it is likely more elegant that all the n forms have a unique prefix. It is important that the prefixes are unique, since otherwise the same problem will arise.

We need to specify the prefix for both the GET and the POST codepath, so that means that the view will look like:

from django.shortcuts import redirect, render

def some_view(request):
    if request.method == 'POST':
        father_form = FatherForm(request.POST, prefix='father')
        mother_form = MotherForm(request.POST, prefix='mother')
        if father_form.is_valid() and mother_form.is_valid():
            # process the data …
            return redirect('name-of-some-view')
    else:
        father_form = FatherForm(prefix='father')
        mother_form = MotherForm(prefix='mother')
    return render(
        request,
        'name-of-some-template.html',
        {'father_form': father_form, 'mother_form': mother_form}
    )

Extra tips

Django has a FormSet class [Django-doc] to render a (large) collection of forms each with a different prefix, this makes it more easy to route data to the correct form without having to worry about the prefixes oneself.