~ubuntu-branches/ubuntu/quantal/python-django/quantal

« back to all changes in this revision

Viewing changes to tests/regressiontests/forms/formsets.py

  • Committer: Bazaar Package Importer
  • Author(s): Scott James Remnant
  • Date: 2008-11-15 19:15:33 UTC
  • mfrom: (1.1.6 upstream)
  • Revision ID: james.westby@ubuntu.com-20081115191533-xbt1ut2xf4fvwtvc
Tags: 1.0.1-0ubuntu1
* New upstream release:
  - Bug fixes.

* The tests/ sub-directory appaers to have been dropped upstream, so pull
  our patch to workaround the tests and modify the rules.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# -*- coding: utf-8 -*-
2
 
tests = """
3
 
# Basic FormSet creation and usage ############################################
4
 
 
5
 
FormSet allows us to use multiple instance of the same form on 1 page. For now,
6
 
the best way to create a FormSet is by using the formset_factory function.
7
 
 
8
 
>>> from django.forms import Form, CharField, IntegerField, ValidationError
9
 
>>> from django.forms.formsets import formset_factory, BaseFormSet
10
 
 
11
 
>>> class Choice(Form):
12
 
...     choice = CharField()
13
 
...     votes = IntegerField()
14
 
 
15
 
>>> ChoiceFormSet = formset_factory(Choice)
16
 
 
17
 
A FormSet constructor takes the same arguments as Form. Let's create a FormSet
18
 
for adding data. By default, it displays 1 blank form. It can display more,
19
 
but we'll look at how to do so later.
20
 
 
21
 
>>> formset = ChoiceFormSet(auto_id=False, prefix='choices')
22
 
>>> print formset
23
 
<input type="hidden" name="choices-TOTAL_FORMS" value="1" /><input type="hidden" name="choices-INITIAL_FORMS" value="0" />
24
 
<tr><th>Choice:</th><td><input type="text" name="choices-0-choice" /></td></tr>
25
 
<tr><th>Votes:</th><td><input type="text" name="choices-0-votes" /></td></tr>
26
 
 
27
 
 
28
 
On thing to note is that there needs to be a special value in the data. This
29
 
value tells the FormSet how many forms were displayed so it can tell how
30
 
many forms it needs to clean and validate. You could use javascript to create
31
 
new forms on the client side, but they won't get validated unless you increment
32
 
the TOTAL_FORMS field appropriately.
33
 
 
34
 
>>> data = {
35
 
...     'choices-TOTAL_FORMS': '1', # the number of forms rendered
36
 
...     'choices-INITIAL_FORMS': '0', # the number of forms with initial data
37
 
...     'choices-0-choice': 'Calexico',
38
 
...     'choices-0-votes': '100',
39
 
... }
40
 
 
41
 
We treat FormSet pretty much like we would treat a normal Form. FormSet has an
42
 
is_valid method, and a cleaned_data or errors attribute depending on whether all
43
 
the forms passed validation. However, unlike a Form instance, cleaned_data and
44
 
errors will be a list of dicts rather than just a single dict.
45
 
 
46
 
>>> formset = ChoiceFormSet(data, auto_id=False, prefix='choices')
47
 
>>> formset.is_valid()
48
 
True
49
 
>>> [form.cleaned_data for form in formset.forms]
50
 
[{'votes': 100, 'choice': u'Calexico'}]
51
 
 
52
 
If a FormSet was not passed any data, its is_valid method should return False.
53
 
>>> formset = ChoiceFormSet()
54
 
>>> formset.is_valid()
55
 
False
56
 
 
57
 
FormSet instances can also have an error attribute if validation failed for
58
 
any of the forms.
59
 
 
60
 
>>> data = {
61
 
...     'choices-TOTAL_FORMS': '1', # the number of forms rendered
62
 
...     'choices-INITIAL_FORMS': '0', # the number of forms with initial data
63
 
...     'choices-0-choice': 'Calexico',
64
 
...     'choices-0-votes': '',
65
 
... }
66
 
 
67
 
>>> formset = ChoiceFormSet(data, auto_id=False, prefix='choices')
68
 
>>> formset.is_valid()
69
 
False
70
 
>>> formset.errors
71
 
[{'votes': [u'This field is required.']}]
72
 
 
73
 
 
74
 
We can also prefill a FormSet with existing data by providing an ``initial``
75
 
argument to the constructor. ``initial`` should be a list of dicts. By default,
76
 
an extra blank form is included.
77
 
 
78
 
>>> initial = [{'choice': u'Calexico', 'votes': 100}]
79
 
>>> formset = ChoiceFormSet(initial=initial, auto_id=False, prefix='choices')
80
 
>>> for form in formset.forms:
81
 
...    print form.as_ul()
82
 
<li>Choice: <input type="text" name="choices-0-choice" value="Calexico" /></li>
83
 
<li>Votes: <input type="text" name="choices-0-votes" value="100" /></li>
84
 
<li>Choice: <input type="text" name="choices-1-choice" /></li>
85
 
<li>Votes: <input type="text" name="choices-1-votes" /></li>
86
 
 
87
 
 
88
 
Let's simulate what would happen if we submitted this form.
89
 
 
90
 
>>> data = {
91
 
...     'choices-TOTAL_FORMS': '2', # the number of forms rendered
92
 
...     'choices-INITIAL_FORMS': '1', # the number of forms with initial data
93
 
...     'choices-0-choice': 'Calexico',
94
 
...     'choices-0-votes': '100',
95
 
...     'choices-1-choice': '',
96
 
...     'choices-1-votes': '',
97
 
... }
98
 
 
99
 
>>> formset = ChoiceFormSet(data, auto_id=False, prefix='choices')
100
 
>>> formset.is_valid()
101
 
True
102
 
>>> [form.cleaned_data for form in formset.forms]
103
 
[{'votes': 100, 'choice': u'Calexico'}, {}]
104
 
 
105
 
But the second form was blank! Shouldn't we get some errors? No. If we display
106
 
a form as blank, it's ok for it to be submitted as blank. If we fill out even
107
 
one of the fields of a blank form though, it will be validated. We may want to
108
 
required that at least x number of forms are completed, but we'll show how to
109
 
handle that later.
110
 
 
111
 
>>> data = {
112
 
...     'choices-TOTAL_FORMS': '2', # the number of forms rendered
113
 
...     'choices-INITIAL_FORMS': '1', # the number of forms with initial data
114
 
...     'choices-0-choice': 'Calexico',
115
 
...     'choices-0-votes': '100',
116
 
...     'choices-1-choice': 'The Decemberists',
117
 
...     'choices-1-votes': '', # missing value
118
 
... }
119
 
 
120
 
>>> formset = ChoiceFormSet(data, auto_id=False, prefix='choices')
121
 
>>> formset.is_valid()
122
 
False
123
 
>>> formset.errors
124
 
[{}, {'votes': [u'This field is required.']}]
125
 
 
126
 
If we delete data that was pre-filled, we should get an error. Simply removing
127
 
data from form fields isn't the proper way to delete it. We'll see how to
128
 
handle that case later.
129
 
 
130
 
>>> data = {
131
 
...     'choices-TOTAL_FORMS': '2', # the number of forms rendered
132
 
...     'choices-INITIAL_FORMS': '1', # the number of forms with initial data
133
 
...     'choices-0-choice': '', # deleted value
134
 
...     'choices-0-votes': '', # deleted value
135
 
...     'choices-1-choice': '',
136
 
...     'choices-1-votes': '',
137
 
... }
138
 
 
139
 
>>> formset = ChoiceFormSet(data, auto_id=False, prefix='choices')
140
 
>>> formset.is_valid()
141
 
False
142
 
>>> formset.errors
143
 
[{'votes': [u'This field is required.'], 'choice': [u'This field is required.']}, {}]
144
 
 
145
 
 
146
 
# Displaying more than 1 blank form ###########################################
147
 
 
148
 
We can also display more than 1 empty form at a time. To do so, pass a
149
 
extra argument to formset_factory.
150
 
 
151
 
>>> ChoiceFormSet = formset_factory(Choice, extra=3)
152
 
 
153
 
>>> formset = ChoiceFormSet(auto_id=False, prefix='choices')
154
 
>>> for form in formset.forms:
155
 
...    print form.as_ul()
156
 
<li>Choice: <input type="text" name="choices-0-choice" /></li>
157
 
<li>Votes: <input type="text" name="choices-0-votes" /></li>
158
 
<li>Choice: <input type="text" name="choices-1-choice" /></li>
159
 
<li>Votes: <input type="text" name="choices-1-votes" /></li>
160
 
<li>Choice: <input type="text" name="choices-2-choice" /></li>
161
 
<li>Votes: <input type="text" name="choices-2-votes" /></li>
162
 
 
163
 
Since we displayed every form as blank, we will also accept them back as blank.
164
 
This may seem a little strange, but later we will show how to require a minimum
165
 
number of forms to be completed.
166
 
 
167
 
>>> data = {
168
 
...     'choices-TOTAL_FORMS': '3', # the number of forms rendered
169
 
...     'choices-INITIAL_FORMS': '0', # the number of forms with initial data
170
 
...     'choices-0-choice': '',
171
 
...     'choices-0-votes': '',
172
 
...     'choices-1-choice': '',
173
 
...     'choices-1-votes': '',
174
 
...     'choices-2-choice': '',
175
 
...     'choices-2-votes': '',
176
 
... }
177
 
 
178
 
>>> formset = ChoiceFormSet(data, auto_id=False, prefix='choices')
179
 
>>> formset.is_valid()
180
 
True
181
 
>>> [form.cleaned_data for form in formset.forms]
182
 
[{}, {}, {}]
183
 
 
184
 
 
185
 
We can just fill out one of the forms.
186
 
 
187
 
>>> data = {
188
 
...     'choices-TOTAL_FORMS': '3', # the number of forms rendered
189
 
...     'choices-INITIAL_FORMS': '0', # the number of forms with initial data
190
 
...     'choices-0-choice': 'Calexico',
191
 
...     'choices-0-votes': '100',
192
 
...     'choices-1-choice': '',
193
 
...     'choices-1-votes': '',
194
 
...     'choices-2-choice': '',
195
 
...     'choices-2-votes': '',
196
 
... }
197
 
 
198
 
>>> formset = ChoiceFormSet(data, auto_id=False, prefix='choices')
199
 
>>> formset.is_valid()
200
 
True
201
 
>>> [form.cleaned_data for form in formset.forms]
202
 
[{'votes': 100, 'choice': u'Calexico'}, {}, {}]
203
 
 
204
 
 
205
 
And once again, if we try to partially complete a form, validation will fail.
206
 
 
207
 
>>> data = {
208
 
...     'choices-TOTAL_FORMS': '3', # the number of forms rendered
209
 
...     'choices-INITIAL_FORMS': '0', # the number of forms with initial data
210
 
...     'choices-0-choice': 'Calexico',
211
 
...     'choices-0-votes': '100',
212
 
...     'choices-1-choice': 'The Decemberists',
213
 
...     'choices-1-votes': '', # missing value
214
 
...     'choices-2-choice': '',
215
 
...     'choices-2-votes': '',
216
 
... }
217
 
 
218
 
>>> formset = ChoiceFormSet(data, auto_id=False, prefix='choices')
219
 
>>> formset.is_valid()
220
 
False
221
 
>>> formset.errors
222
 
[{}, {'votes': [u'This field is required.']}, {}]
223
 
 
224
 
 
225
 
The extra argument also works when the formset is pre-filled with initial
226
 
data.
227
 
 
228
 
>>> initial = [{'choice': u'Calexico', 'votes': 100}]
229
 
>>> formset = ChoiceFormSet(initial=initial, auto_id=False, prefix='choices')
230
 
>>> for form in formset.forms:
231
 
...    print form.as_ul()
232
 
<li>Choice: <input type="text" name="choices-0-choice" value="Calexico" /></li>
233
 
<li>Votes: <input type="text" name="choices-0-votes" value="100" /></li>
234
 
<li>Choice: <input type="text" name="choices-1-choice" /></li>
235
 
<li>Votes: <input type="text" name="choices-1-votes" /></li>
236
 
<li>Choice: <input type="text" name="choices-2-choice" /></li>
237
 
<li>Votes: <input type="text" name="choices-2-votes" /></li>
238
 
<li>Choice: <input type="text" name="choices-3-choice" /></li>
239
 
<li>Votes: <input type="text" name="choices-3-votes" /></li>
240
 
 
241
 
 
242
 
# FormSets with deletion ######################################################
243
 
 
244
 
We can easily add deletion ability to a FormSet with an agrument to
245
 
formset_factory. This will add a boolean field to each form instance. When
246
 
that boolean field is True, the form will be in formset.deleted_forms
247
 
 
248
 
>>> ChoiceFormSet = formset_factory(Choice, can_delete=True)
249
 
 
250
 
>>> initial = [{'choice': u'Calexico', 'votes': 100}, {'choice': u'Fergie', 'votes': 900}]
251
 
>>> formset = ChoiceFormSet(initial=initial, auto_id=False, prefix='choices')
252
 
>>> for form in formset.forms:
253
 
...    print form.as_ul()
254
 
<li>Choice: <input type="text" name="choices-0-choice" value="Calexico" /></li>
255
 
<li>Votes: <input type="text" name="choices-0-votes" value="100" /></li>
256
 
<li>Delete: <input type="checkbox" name="choices-0-DELETE" /></li>
257
 
<li>Choice: <input type="text" name="choices-1-choice" value="Fergie" /></li>
258
 
<li>Votes: <input type="text" name="choices-1-votes" value="900" /></li>
259
 
<li>Delete: <input type="checkbox" name="choices-1-DELETE" /></li>
260
 
<li>Choice: <input type="text" name="choices-2-choice" /></li>
261
 
<li>Votes: <input type="text" name="choices-2-votes" /></li>
262
 
<li>Delete: <input type="checkbox" name="choices-2-DELETE" /></li>
263
 
 
264
 
To delete something, we just need to set that form's special delete field to
265
 
'on'. Let's go ahead and delete Fergie.
266
 
 
267
 
>>> data = {
268
 
...     'choices-TOTAL_FORMS': '3', # the number of forms rendered
269
 
...     'choices-INITIAL_FORMS': '2', # the number of forms with initial data
270
 
...     'choices-0-choice': 'Calexico',
271
 
...     'choices-0-votes': '100',
272
 
...     'choices-0-DELETE': '',
273
 
...     'choices-1-choice': 'Fergie',
274
 
...     'choices-1-votes': '900',
275
 
...     'choices-1-DELETE': 'on',
276
 
...     'choices-2-choice': '',
277
 
...     'choices-2-votes': '',
278
 
...     'choices-2-DELETE': '',
279
 
... }
280
 
 
281
 
>>> formset = ChoiceFormSet(data, auto_id=False, prefix='choices')
282
 
>>> formset.is_valid()
283
 
True
284
 
>>> [form.cleaned_data for form in formset.forms]
285
 
[{'votes': 100, 'DELETE': False, 'choice': u'Calexico'}, {'votes': 900, 'DELETE': True, 'choice': u'Fergie'}, {}]
286
 
>>> [form.cleaned_data for form in formset.deleted_forms]
287
 
[{'votes': 900, 'DELETE': True, 'choice': u'Fergie'}]
288
 
 
289
 
 
290
 
# FormSets with ordering ######################################################
291
 
 
292
 
We can also add ordering ability to a FormSet with an agrument to
293
 
formset_factory. This will add a integer field to each form instance. When
294
 
form validation succeeds, [form.cleaned_data for form in formset.forms] will have the data in the correct
295
 
order specified by the ordering fields. If a number is duplicated in the set
296
 
of ordering fields, for instance form 0 and form 3 are both marked as 1, then
297
 
the form index used as a secondary ordering criteria. In order to put
298
 
something at the front of the list, you'd need to set it's order to 0.
299
 
 
300
 
>>> ChoiceFormSet = formset_factory(Choice, can_order=True)
301
 
 
302
 
>>> initial = [{'choice': u'Calexico', 'votes': 100}, {'choice': u'Fergie', 'votes': 900}]
303
 
>>> formset = ChoiceFormSet(initial=initial, auto_id=False, prefix='choices')
304
 
>>> for form in formset.forms:
305
 
...    print form.as_ul()
306
 
<li>Choice: <input type="text" name="choices-0-choice" value="Calexico" /></li>
307
 
<li>Votes: <input type="text" name="choices-0-votes" value="100" /></li>
308
 
<li>Order: <input type="text" name="choices-0-ORDER" value="1" /></li>
309
 
<li>Choice: <input type="text" name="choices-1-choice" value="Fergie" /></li>
310
 
<li>Votes: <input type="text" name="choices-1-votes" value="900" /></li>
311
 
<li>Order: <input type="text" name="choices-1-ORDER" value="2" /></li>
312
 
<li>Choice: <input type="text" name="choices-2-choice" /></li>
313
 
<li>Votes: <input type="text" name="choices-2-votes" /></li>
314
 
<li>Order: <input type="text" name="choices-2-ORDER" /></li>
315
 
 
316
 
>>> data = {
317
 
...     'choices-TOTAL_FORMS': '3', # the number of forms rendered
318
 
...     'choices-INITIAL_FORMS': '2', # the number of forms with initial data
319
 
...     'choices-0-choice': 'Calexico',
320
 
...     'choices-0-votes': '100',
321
 
...     'choices-0-ORDER': '1',
322
 
...     'choices-1-choice': 'Fergie',
323
 
...     'choices-1-votes': '900',
324
 
...     'choices-1-ORDER': '2',
325
 
...     'choices-2-choice': 'The Decemberists',
326
 
...     'choices-2-votes': '500',
327
 
...     'choices-2-ORDER': '0',
328
 
... }
329
 
 
330
 
>>> formset = ChoiceFormSet(data, auto_id=False, prefix='choices')
331
 
>>> formset.is_valid()
332
 
True
333
 
>>> for form in formset.ordered_forms:
334
 
...    print form.cleaned_data
335
 
{'votes': 500, 'ORDER': 0, 'choice': u'The Decemberists'}
336
 
{'votes': 100, 'ORDER': 1, 'choice': u'Calexico'}
337
 
{'votes': 900, 'ORDER': 2, 'choice': u'Fergie'}
338
 
 
339
 
Ordering fields are allowed to be left blank, and if they *are* left blank,
340
 
they will be sorted below everything else.
341
 
 
342
 
>>> data = {
343
 
...     'choices-TOTAL_FORMS': '4', # the number of forms rendered
344
 
...     'choices-INITIAL_FORMS': '3', # the number of forms with initial data
345
 
...     'choices-0-choice': 'Calexico',
346
 
...     'choices-0-votes': '100',
347
 
...     'choices-0-ORDER': '1',
348
 
...     'choices-1-choice': 'Fergie',
349
 
...     'choices-1-votes': '900',
350
 
...     'choices-1-ORDER': '2',
351
 
...     'choices-2-choice': 'The Decemberists',
352
 
...     'choices-2-votes': '500',
353
 
...     'choices-2-ORDER': '',
354
 
...     'choices-3-choice': 'Basia Bulat',
355
 
...     'choices-3-votes': '50',
356
 
...     'choices-3-ORDER': '',
357
 
... }
358
 
 
359
 
>>> formset = ChoiceFormSet(data, auto_id=False, prefix='choices')
360
 
>>> formset.is_valid()
361
 
True
362
 
>>> for form in formset.ordered_forms:
363
 
...    print form.cleaned_data
364
 
{'votes': 100, 'ORDER': 1, 'choice': u'Calexico'}
365
 
{'votes': 900, 'ORDER': 2, 'choice': u'Fergie'}
366
 
{'votes': 500, 'ORDER': None, 'choice': u'The Decemberists'}
367
 
{'votes': 50, 'ORDER': None, 'choice': u'Basia Bulat'}
368
 
 
369
 
 
370
 
# FormSets with ordering + deletion ###########################################
371
 
 
372
 
Let's try throwing ordering and deletion into the same form.
373
 
 
374
 
>>> ChoiceFormSet = formset_factory(Choice, can_order=True, can_delete=True)
375
 
 
376
 
>>> initial = [
377
 
...     {'choice': u'Calexico', 'votes': 100},
378
 
...     {'choice': u'Fergie', 'votes': 900},
379
 
...     {'choice': u'The Decemberists', 'votes': 500},
380
 
... ]
381
 
>>> formset = ChoiceFormSet(initial=initial, auto_id=False, prefix='choices')
382
 
>>> for form in formset.forms:
383
 
...    print form.as_ul()
384
 
<li>Choice: <input type="text" name="choices-0-choice" value="Calexico" /></li>
385
 
<li>Votes: <input type="text" name="choices-0-votes" value="100" /></li>
386
 
<li>Order: <input type="text" name="choices-0-ORDER" value="1" /></li>
387
 
<li>Delete: <input type="checkbox" name="choices-0-DELETE" /></li>
388
 
<li>Choice: <input type="text" name="choices-1-choice" value="Fergie" /></li>
389
 
<li>Votes: <input type="text" name="choices-1-votes" value="900" /></li>
390
 
<li>Order: <input type="text" name="choices-1-ORDER" value="2" /></li>
391
 
<li>Delete: <input type="checkbox" name="choices-1-DELETE" /></li>
392
 
<li>Choice: <input type="text" name="choices-2-choice" value="The Decemberists" /></li>
393
 
<li>Votes: <input type="text" name="choices-2-votes" value="500" /></li>
394
 
<li>Order: <input type="text" name="choices-2-ORDER" value="3" /></li>
395
 
<li>Delete: <input type="checkbox" name="choices-2-DELETE" /></li>
396
 
<li>Choice: <input type="text" name="choices-3-choice" /></li>
397
 
<li>Votes: <input type="text" name="choices-3-votes" /></li>
398
 
<li>Order: <input type="text" name="choices-3-ORDER" /></li>
399
 
<li>Delete: <input type="checkbox" name="choices-3-DELETE" /></li>
400
 
 
401
 
Let's delete Fergie, and put The Decemberists ahead of Calexico.
402
 
 
403
 
>>> data = {
404
 
...     'choices-TOTAL_FORMS': '4', # the number of forms rendered
405
 
...     'choices-INITIAL_FORMS': '3', # the number of forms with initial data
406
 
...     'choices-0-choice': 'Calexico',
407
 
...     'choices-0-votes': '100',
408
 
...     'choices-0-ORDER': '1',
409
 
...     'choices-0-DELETE': '',
410
 
...     'choices-1-choice': 'Fergie',
411
 
...     'choices-1-votes': '900',
412
 
...     'choices-1-ORDER': '2',
413
 
...     'choices-1-DELETE': 'on',
414
 
...     'choices-2-choice': 'The Decemberists',
415
 
...     'choices-2-votes': '500',
416
 
...     'choices-2-ORDER': '0',
417
 
...     'choices-2-DELETE': '',
418
 
...     'choices-3-choice': '',
419
 
...     'choices-3-votes': '',
420
 
...     'choices-3-ORDER': '',
421
 
...     'choices-3-DELETE': '',
422
 
... }
423
 
 
424
 
>>> formset = ChoiceFormSet(data, auto_id=False, prefix='choices')
425
 
>>> formset.is_valid()
426
 
True
427
 
>>> for form in formset.ordered_forms:
428
 
...    print form.cleaned_data
429
 
{'votes': 500, 'DELETE': False, 'ORDER': 0, 'choice': u'The Decemberists'}
430
 
{'votes': 100, 'DELETE': False, 'ORDER': 1, 'choice': u'Calexico'}
431
 
>>> [form.cleaned_data for form in formset.deleted_forms]
432
 
[{'votes': 900, 'DELETE': True, 'ORDER': 2, 'choice': u'Fergie'}]
433
 
 
434
 
 
435
 
# FormSet clean hook ##########################################################
436
 
 
437
 
FormSets have a hook for doing extra validation that shouldn't be tied to any
438
 
particular form. It follows the same pattern as the clean hook on Forms.
439
 
 
440
 
Let's define a FormSet that takes a list of favorite drinks, but raises am
441
 
error if there are any duplicates.
442
 
 
443
 
>>> class FavoriteDrinkForm(Form):
444
 
...     name = CharField()
445
 
...
446
 
 
447
 
>>> class BaseFavoriteDrinksFormSet(BaseFormSet):
448
 
...     def clean(self):
449
 
...         seen_drinks = []
450
 
...         for drink in self.cleaned_data:
451
 
...             if drink['name'] in seen_drinks:
452
 
...                 raise ValidationError('You may only specify a drink once.')
453
 
...             seen_drinks.append(drink['name'])
454
 
...
455
 
 
456
 
>>> FavoriteDrinksFormSet = formset_factory(FavoriteDrinkForm,
457
 
...     formset=BaseFavoriteDrinksFormSet, extra=3)
458
 
 
459
 
We start out with a some duplicate data.
460
 
 
461
 
>>> data = {
462
 
...     'drinks-TOTAL_FORMS': '2', # the number of forms rendered
463
 
...     'drinks-INITIAL_FORMS': '0', # the number of forms with initial data
464
 
...     'drinks-0-name': 'Gin and Tonic',
465
 
...     'drinks-1-name': 'Gin and Tonic',
466
 
... }
467
 
 
468
 
>>> formset = FavoriteDrinksFormSet(data, prefix='drinks')
469
 
>>> formset.is_valid()
470
 
False
471
 
 
472
 
Any errors raised by formset.clean() are available via the
473
 
formset.non_form_errors() method.
474
 
 
475
 
>>> for error in formset.non_form_errors():
476
 
...     print error
477
 
You may only specify a drink once.
478
 
 
479
 
 
480
 
Make sure we didn't break the valid case.
481
 
 
482
 
>>> data = {
483
 
...     'drinks-TOTAL_FORMS': '2', # the number of forms rendered
484
 
...     'drinks-INITIAL_FORMS': '0', # the number of forms with initial data
485
 
...     'drinks-0-name': 'Gin and Tonic',
486
 
...     'drinks-1-name': 'Bloody Mary',
487
 
... }
488
 
 
489
 
>>> formset = FavoriteDrinksFormSet(data, prefix='drinks')
490
 
>>> formset.is_valid()
491
 
True
492
 
>>> for error in formset.non_form_errors():
493
 
...     print error
494
 
 
495
 
# Limiting the maximum number of forms ########################################
496
 
 
497
 
# Base case for max_num.
498
 
 
499
 
>>> LimitedFavoriteDrinkFormSet = formset_factory(FavoriteDrinkForm, extra=5, max_num=2)
500
 
>>> formset = LimitedFavoriteDrinkFormSet()
501
 
>>> for form in formset.forms:
502
 
...     print form
503
 
<tr><th><label for="id_form-0-name">Name:</label></th><td><input type="text" name="form-0-name" id="id_form-0-name" /></td></tr>
504
 
<tr><th><label for="id_form-1-name">Name:</label></th><td><input type="text" name="form-1-name" id="id_form-1-name" /></td></tr>
505
 
 
506
 
# Ensure the that max_num has no affect when extra is less than max_forms.
507
 
 
508
 
>>> LimitedFavoriteDrinkFormSet = formset_factory(FavoriteDrinkForm, extra=1, max_num=2)
509
 
>>> formset = LimitedFavoriteDrinkFormSet()
510
 
>>> for form in formset.forms:
511
 
...     print form
512
 
<tr><th><label for="id_form-0-name">Name:</label></th><td><input type="text" name="form-0-name" id="id_form-0-name" /></td></tr>
513
 
 
514
 
# max_num with initial data
515
 
 
516
 
# More initial forms than max_num will result in only the first max_num of
517
 
# them to be displayed with no extra forms.
518
 
 
519
 
>>> initial = [
520
 
...     {'name': 'Gin Tonic'},
521
 
...     {'name': 'Bloody Mary'},
522
 
...     {'name': 'Jack and Coke'},
523
 
... ]
524
 
>>> LimitedFavoriteDrinkFormSet = formset_factory(FavoriteDrinkForm, extra=1, max_num=2)
525
 
>>> formset = LimitedFavoriteDrinkFormSet(initial=initial)
526
 
>>> for form in formset.forms:
527
 
...     print form
528
 
<tr><th><label for="id_form-0-name">Name:</label></th><td><input type="text" name="form-0-name" value="Gin Tonic" id="id_form-0-name" /></td></tr>
529
 
<tr><th><label for="id_form-1-name">Name:</label></th><td><input type="text" name="form-1-name" value="Bloody Mary" id="id_form-1-name" /></td></tr>
530
 
 
531
 
# One form from initial and extra=3 with max_num=2 should result in the one
532
 
# initial form and one extra.
533
 
 
534
 
>>> initial = [
535
 
...     {'name': 'Gin Tonic'},
536
 
... ]
537
 
>>> LimitedFavoriteDrinkFormSet = formset_factory(FavoriteDrinkForm, extra=3, max_num=2)
538
 
>>> formset = LimitedFavoriteDrinkFormSet(initial=initial)
539
 
>>> for form in formset.forms:
540
 
...     print form
541
 
<tr><th><label for="id_form-0-name">Name:</label></th><td><input type="text" name="form-0-name" value="Gin Tonic" id="id_form-0-name" /></td></tr>
542
 
<tr><th><label for="id_form-1-name">Name:</label></th><td><input type="text" name="form-1-name" id="id_form-1-name" /></td></tr>
543
 
 
544
 
 
545
 
# Regression test for #6926 ##################################################
546
 
 
547
 
Make sure the management form has the correct prefix.
548
 
 
549
 
>>> formset = FavoriteDrinksFormSet()
550
 
>>> formset.management_form.prefix
551
 
'form'
552
 
 
553
 
>>> formset = FavoriteDrinksFormSet(data={})
554
 
>>> formset.management_form.prefix
555
 
'form'
556
 
 
557
 
>>> formset = FavoriteDrinksFormSet(initial={})
558
 
>>> formset.management_form.prefix
559
 
'form'
560
 
 
561
 
"""