1
.. _topics-forms-formsets:
7
A formset is a layer of abstraction to working with multiple forms on the same
8
page. It can be best compared to a data grid. Let's say you have the following
11
>>> from django import forms
12
>>> class ArticleForm(forms.Form):
13
... title = forms.CharField()
14
... pub_date = forms.DateField()
16
You might want to allow the user to create several articles at once. To create
17
a formset out of an ``ArticleForm`` you would do::
19
>>> from django.forms.formsets import formset_factory
20
>>> ArticleFormSet = formset_factory(ArticleForm)
22
You now have created a formset named ``ArticleFormSet``. The formset gives you
23
the ability to iterate over the forms in the formset and display them as you
24
would with a regular form::
26
>>> formset = ArticleFormSet()
27
>>> for form in formset.forms:
28
... print form.as_table()
29
<tr><th><label for="id_form-0-title">Title:</label></th><td><input type="text" name="form-0-title" id="id_form-0-title" /></td></tr>
30
<tr><th><label for="id_form-0-pub_date">Pub date:</label></th><td><input type="text" name="form-0-pub_date" id="id_form-0-pub_date" /></td></tr>
32
As you can see it only displayed one form. This is because by default the
33
``formset_factory`` defines one extra form. This can be controlled with the
36
>>> ArticleFormSet = formset_factory(ArticleForm, extra=2)
38
Using initial data with a formset
39
---------------------------------
41
Initial data is what drives the main usability of a formset. As shown above
42
you can define the number of extra forms. What this means is that you are
43
telling the formset how many additional forms to show in addition to the
44
number of forms it generates from the initial data. Lets take a look at an
47
>>> ArticleFormSet = formset_factory(ArticleForm, extra=2)
48
>>> formset = ArticleFormSet(initial=[
49
... {'title': u'Django is now open source',
50
... 'pub_date': datetime.date.today()},
53
>>> for form in formset.forms:
54
... print form.as_table()
55
<tr><th><label for="id_form-0-title">Title:</label></th><td><input type="text" name="form-0-title" value="Django is now open source" id="id_form-0-title" /></td></tr>
56
<tr><th><label for="id_form-0-pub_date">Pub date:</label></th><td><input type="text" name="form-0-pub_date" value="2008-05-12" id="id_form-0-pub_date" /></td></tr>
57
<tr><th><label for="id_form-1-title">Title:</label></th><td><input type="text" name="form-1-title" id="id_form-1-title" /></td></tr>
58
<tr><th><label for="id_form-1-pub_date">Pub date:</label></th><td><input type="text" name="form-1-pub_date" id="id_form-1-pub_date" /></td></tr>
59
<tr><th><label for="id_form-2-title">Title:</label></th><td><input type="text" name="form-2-title" id="id_form-2-title" /></td></tr>
60
<tr><th><label for="id_form-2-pub_date">Pub date:</label></th><td><input type="text" name="form-2-pub_date" id="id_form-2-pub_date" /></td></tr>
62
There are now a total of three forms showing above. One for the initial data
63
that was passed in and two extra forms. Also note that we are passing in a
64
list of dictionaries as the initial data.
66
Limiting the maximum number of forms
67
------------------------------------
69
The ``max_num`` parameter to ``formset_factory`` gives you the ability to
70
force the maximum number of forms the formset will display::
72
>>> ArticleFormSet = formset_factory(ArticleForm, extra=2, max_num=1)
73
>>> formset = ArticleFormset()
74
>>> for form in formset.forms:
75
... print form.as_table()
76
<tr><th><label for="id_form-0-title">Title:</label></th><td><input type="text" name="form-0-title" id="id_form-0-title" /></td></tr>
77
<tr><th><label for="id_form-0-pub_date">Pub date:</label></th><td><input type="text" name="form-0-pub_date" id="id_form-0-pub_date" /></td></tr>
79
The default value of ``max_num`` is ``0`` which is the same as saying put no
80
limit on the number forms displayed.
85
Validation with a formset is about identical to a regular ``Form``. There is
86
an ``is_valid`` method on the formset to provide a convenient way to validate
87
each form in the formset::
89
>>> ArticleFormSet = formset_factory(ArticleForm)
90
>>> formset = ArticleFormSet({})
91
>>> formset.is_valid()
94
We passed in no data to the formset which is resulting in a valid form. The
95
formset is smart enough to ignore extra forms that were not changed. If we
96
attempt to provide an article, but fail to do so::
99
... 'form-TOTAL_FORMS': u'1',
100
... 'form-INITIAL_FORMS': u'1',
101
... 'form-0-title': u'Test',
102
... 'form-0-pub_date': u'',
104
>>> formset = ArticleFormSet(data)
105
>>> formset.is_valid()
108
[{'pub_date': [u'This field is required.']}]
110
As we can see the formset properly performed validation and gave us the
113
Understanding the ManagementForm
114
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
116
You may have noticed the additional data that was required in the formset's
117
data above. This data is coming from the ``ManagementForm``. This form is
118
dealt with internally to the formset. If you don't use it, it will result in
122
... 'form-0-title': u'Test',
123
... 'form-0-pub_date': u'',
125
>>> formset = ArticleFormSet(data)
126
Traceback (most recent call last):
128
django.forms.util.ValidationError: [u'ManagementForm data is missing or has been tampered with']
130
It is used to keep track of how many form instances are being displayed. If
131
you are adding new forms via JavaScript, you should increment the count fields
132
in this form as well.
134
Custom formset validation
135
~~~~~~~~~~~~~~~~~~~~~~~~~
137
A formset has a ``clean`` method similar to the one on a ``Form`` class. This
138
is where you define your own validation that deals at the formset level::
140
>>> from django.forms.formsets import BaseFormSet
142
>>> class BaseArticleFormSet(BaseFormSet):
144
... raise forms.ValidationError, u'An error occured.'
146
>>> ArticleFormSet = formset_factory(ArticleForm, formset=BaseArticleFormSet)
147
>>> formset = ArticleFormSet({})
148
>>> formset.is_valid()
150
>>> formset.non_form_errors()
151
[u'An error occured.']
153
The formset ``clean`` method is called after all the ``Form.clean`` methods
154
have been called. The errors will be found using the ``non_form_errors()``
155
method on the formset.
157
Dealing with ordering and deletion of forms
158
-------------------------------------------
160
Common use cases with a formset is dealing with ordering and deletion of the
161
form instances. This has been dealt with for you. The ``formset_factory``
162
provides two optional parameters ``can_order`` and ``can_delete`` that will do
163
the extra work of adding the extra fields and providing simpler ways of
164
getting to that data.
171
Lets create a formset with the ability to order::
173
>>> ArticleFormSet = formset_factory(ArticleForm, can_order=True)
174
>>> formset = ArticleFormSet(initial=[
175
... {'title': u'Article #1', 'pub_date': datetime.date(2008, 5, 10)},
176
... {'title': u'Article #2', 'pub_date': datetime.date(2008, 5, 11)},
178
>>> for form in formset.forms:
179
... print form.as_table()
180
<tr><th><label for="id_form-0-title">Title:</label></th><td><input type="text" name="form-0-title" value="Article #1" id="id_form-0-title" /></td></tr>
181
<tr><th><label for="id_form-0-pub_date">Pub date:</label></th><td><input type="text" name="form-0-pub_date" value="2008-05-10" id="id_form-0-pub_date" /></td></tr>
182
<tr><th><label for="id_form-0-ORDER">Order:</label></th><td><input type="text" name="form-0-ORDER" value="1" id="id_form-0-ORDER" /></td></tr>
183
<tr><th><label for="id_form-1-title">Title:</label></th><td><input type="text" name="form-1-title" value="Article #2" id="id_form-1-title" /></td></tr>
184
<tr><th><label for="id_form-1-pub_date">Pub date:</label></th><td><input type="text" name="form-1-pub_date" value="2008-05-11" id="id_form-1-pub_date" /></td></tr>
185
<tr><th><label for="id_form-1-ORDER">Order:</label></th><td><input type="text" name="form-1-ORDER" value="2" id="id_form-1-ORDER" /></td></tr>
186
<tr><th><label for="id_form-2-title">Title:</label></th><td><input type="text" name="form-2-title" id="id_form-2-title" /></td></tr>
187
<tr><th><label for="id_form-2-pub_date">Pub date:</label></th><td><input type="text" name="form-2-pub_date" id="id_form-2-pub_date" /></td></tr>
188
<tr><th><label for="id_form-2-ORDER">Order:</label></th><td><input type="text" name="form-2-ORDER" id="id_form-2-ORDER" /></td></tr>
190
This adds an additional field to each form. This new field is named ``ORDER``
191
and is an ``forms.IntegerField``. For the forms that came from the initial
192
data it automatically assigned them a numeric value. Lets look at what will
193
happen when the user changes these values::
196
... 'form-TOTAL_FORMS': u'3',
197
... 'form-INITIAL_FORMS': u'2',
198
... 'form-0-title': u'Article #1',
199
... 'form-0-pub_date': u'2008-05-10',
200
... 'form-0-ORDER': u'2',
201
... 'form-1-title': u'Article #2',
202
... 'form-1-pub_date': u'2008-05-11',
203
... 'form-1-ORDER': u'1',
204
... 'form-2-title': u'Article #3',
205
... 'form-2-pub_date': u'2008-05-01',
206
... 'form-2-ORDER': u'0',
209
>>> formset = ArticleFormSet(data, initial=[
210
... {'title': u'Article #1', 'pub_date': datetime.date(2008, 5, 10)},
211
... {'title': u'Article #2', 'pub_date': datetime.date(2008, 5, 11)},
213
>>> formset.is_valid()
215
>>> for form in formset.ordered_forms:
216
... print form.cleaned_data
217
{'pub_date': datetime.date(2008, 5, 1), 'ORDER': 0, 'title': u'Article #3'}
218
{'pub_date': datetime.date(2008, 5, 11), 'ORDER': 1, 'title': u'Article #2'}
219
{'pub_date': datetime.date(2008, 5, 10), 'ORDER': 2, 'title': u'Article #1'}
226
Lets create a formset with the ability to delete::
228
>>> ArticleFormSet = formset_factory(ArticleForm, can_delete=True)
229
>>> formset = ArticleFormSet(initial=[
230
... {'title': u'Article #1', 'pub_date': datetime.date(2008, 5, 10)},
231
... {'title': u'Article #2', 'pub_date': datetime.date(2008, 5, 11)},
233
>>> for form in formset.forms:
234
.... print form.as_table()
235
<input type="hidden" name="form-TOTAL_FORMS" value="3" id="id_form-TOTAL_FORMS" /><input type="hidden" name="form-INITIAL_FORMS" value="2" id="id_form-INITIAL_FORMS" />
236
<tr><th><label for="id_form-0-title">Title:</label></th><td><input type="text" name="form-0-title" value="Article #1" id="id_form-0-title" /></td></tr>
237
<tr><th><label for="id_form-0-pub_date">Pub date:</label></th><td><input type="text" name="form-0-pub_date" value="2008-05-10" id="id_form-0-pub_date" /></td></tr>
238
<tr><th><label for="id_form-0-DELETE">Delete:</label></th><td><input type="checkbox" name="form-0-DELETE" id="id_form-0-DELETE" /></td></tr>
239
<tr><th><label for="id_form-1-title">Title:</label></th><td><input type="text" name="form-1-title" value="Article #2" id="id_form-1-title" /></td></tr>
240
<tr><th><label for="id_form-1-pub_date">Pub date:</label></th><td><input type="text" name="form-1-pub_date" value="2008-05-11" id="id_form-1-pub_date" /></td></tr>
241
<tr><th><label for="id_form-1-DELETE">Delete:</label></th><td><input type="checkbox" name="form-1-DELETE" id="id_form-1-DELETE" /></td></tr>
242
<tr><th><label for="id_form-2-title">Title:</label></th><td><input type="text" name="form-2-title" id="id_form-2-title" /></td></tr>
243
<tr><th><label for="id_form-2-pub_date">Pub date:</label></th><td><input type="text" name="form-2-pub_date" id="id_form-2-pub_date" /></td></tr>
244
<tr><th><label for="id_form-2-DELETE">Delete:</label></th><td><input type="checkbox" name="form-2-DELETE" id="id_form-2-DELETE" /></td></tr>
246
Similar to ``can_order`` this adds a new field to each form named ``DELETE``
247
and is a ``forms.BooleanField``. When data comes through marking any of the
248
delete fields you can access them with ``deleted_forms``::
251
... 'form-TOTAL_FORMS': u'3',
252
... 'form-INITIAL_FORMS': u'2',
253
... 'form-0-title': u'Article #1',
254
... 'form-0-pub_date': u'2008-05-10',
255
... 'form-0-DELETE': u'on',
256
... 'form-1-title': u'Article #2',
257
... 'form-1-pub_date': u'2008-05-11',
258
... 'form-1-DELETE': u'',
259
... 'form-2-title': u'',
260
... 'form-2-pub_date': u'',
261
... 'form-2-DELETE': u'',
264
>>> formset = ArticleFormSet(data, initial=[
265
... {'title': u'Article #1', 'pub_date': datetime.date(2008, 5, 10)},
266
... {'title': u'Article #2', 'pub_date': datetime.date(2008, 5, 11)},
268
>>> [form.cleaned_data for form in formset.deleted_forms]
269
[{'DELETE': True, 'pub_date': datetime.date(2008, 5, 10), 'title': u'Article #1'}]
271
Adding additional fields to a formset
272
-------------------------------------
274
If you need to add additional fields to the formset this can be easily
275
accomplished. The formset base class provides an ``add_fields`` method. You
276
can simply override this method to add your own fields or even redefine the
277
default fields/attributes of the order and deletion fields::
279
>>> class BaseArticleFormSet(BaseFormSet):
280
... def add_fields(self, form, index):
281
... super(BaseArticleFormSet, self).add_fields(form, index)
282
... form.fields["my_field"] = forms.CharField()
284
>>> ArticleFormSet = formset_factory(ArticleForm, formset=BaseArticleFormSet)
285
>>> formset = ArticleFormSet()
286
>>> for form in formset.forms:
287
... print form.as_table()
288
<tr><th><label for="id_form-0-title">Title:</label></th><td><input type="text" name="form-0-title" id="id_form-0-title" /></td></tr>
289
<tr><th><label for="id_form-0-pub_date">Pub date:</label></th><td><input type="text" name="form-0-pub_date" id="id_form-0-pub_date" /></td></tr>
290
<tr><th><label for="id_form-0-my_field">My field:</label></th><td><input type="text" name="form-0-my_field" id="id_form-0-my_field" /></td></tr>
292
Using a formset in views and templates
293
--------------------------------------
295
Using a formset inside a view is as easy as using a regular ``Form`` class.
296
The only thing you will want to be aware of is making sure to use the
297
management form inside the template. Lets look at a sample view::
299
def manage_articles(request):
300
ArticleFormSet = formset_factory(ArticleForm)
301
if request.method == 'POST':
302
formset = ArticleFormSet(request.POST, request.FILES)
303
if formset.is_valid():
304
# do something with the formset.cleaned_data
306
formset = ArticleFormSet()
307
return render_to_response('manage_articles.html', {'formset': formset})
309
The ``manage_articles.html`` template might look like this::
311
<form method="POST" action="">
312
{{ formset.management_form }}
314
{% for form in formset.forms %}
320
However the above can be slightly shortcutted and let the formset itself deal
321
with the management form::
323
<form method="POST" action="">
329
The above ends up calling the ``as_table`` method on the formset class.
331
Using more than one formset in a view
332
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
334
You are able to use more than one formset in a view if you like. Formsets
335
borrow much of its behavior from forms. With that said you are able to use
336
``prefix`` to prefix formset form field names with a given value to allow
337
more than one formset to be sent to a view without name clashing. Lets take
338
a look at how this might be accomplished::
340
def manage_articles(request):
341
ArticleFormSet = formset_factory(ArticleForm)
342
BookFormSet = formset_factory(BookForm)
343
if request.method == 'POST':
344
article_formset = ArticleFormSet(request.POST, request.FILES, prefix='articles')
345
book_formset = BookFormSet(request.POST, request.FILES, prefix='books')
346
if article_formset.is_valid() and book_formset.is_valid():
347
# do something with the cleaned_data on the formsets.
349
article_formset = ArticleFormSet(prefix='articles')
350
book_formset = BookFormSet(prefix='books')
351
return render_to_response('manage_articles.html', {
352
'article_formset': article_formset,
353
'book_formset': book_formset,
356
You would then render the formsets as normal. It is important to point out
357
that you need to pass ``prefix`` on both the POST and non-POST cases so that
358
it is rendered and processed correctly.