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):
= forms.CharField()
name = forms.CharField()
first_name
class MotherForm(forms.Form):
= forms.CharField()
name = forms.CharField() first_name
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.