~ubuntu-branches/debian/sid/python-django/sid

« back to all changes in this revision

Viewing changes to tests/forms_tests/tests/test_formsets.py

  • Committer: Package Import Robot
  • Author(s): Raphaël Hertzog
  • Date: 2014-09-17 14:15:11 UTC
  • mfrom: (1.3.17) (6.2.18 experimental)
  • Revision ID: package-import@ubuntu.com-20140917141511-icneokthe9ww5sk4
Tags: 1.7-2
* Release to unstable.
* Add a migrate-south sample script to help users apply their South
  migrations. Thanks to Brian May.

Show diffs side-by-side

added added

removed removed

Lines of Context:
6
6
from django.forms import (CharField, DateField, FileField, Form, IntegerField,
7
7
    SplitDateTimeField, ValidationError, formsets)
8
8
from django.forms.formsets import BaseFormSet, formset_factory
9
 
from django.forms.util import ErrorList
 
9
from django.forms.utils import ErrorList
10
10
from django.test import TestCase
11
11
 
12
12
 
57
57
class FormsFormsetTestCase(TestCase):
58
58
 
59
59
    def make_choiceformset(self, formset_data=None, formset_class=ChoiceFormSet,
60
 
        total_forms=None, initial_forms=0, max_num_forms=0, **kwargs):
 
60
            total_forms=None, initial_forms=0, max_num_forms=0, min_num_forms=0, **kwargs):
61
61
        """
62
62
        Make a ChoiceFormset from the given formset_data.
63
63
        The data should be given as a list of (choice, votes) tuples.
79
79
            prefixed('TOTAL_FORMS'): str(total_forms),
80
80
            prefixed('INITIAL_FORMS'): str(initial_forms),
81
81
            prefixed('MAX_NUM_FORMS'): str(max_num_forms),
 
82
            prefixed('MIN_NUM_FORMS'): str(min_num_forms),
82
83
        }
83
84
        for i, (choice, votes) in enumerate(formset_data):
84
85
            data[prefixed(str(i), 'choice')] = choice
91
92
        # for adding data. By default, it displays 1 blank form. It can display more,
92
93
        # but we'll look at how to do so later.
93
94
        formset = self.make_choiceformset()
94
 
        
95
 
        self.assertHTMLEqual(str(formset), """<input type="hidden" name="choices-TOTAL_FORMS" value="1" /><input type="hidden" name="choices-INITIAL_FORMS" value="0" /><input type="hidden" name="choices-MAX_NUM_FORMS" value="1000" />
 
95
        self.assertHTMLEqual(str(formset), """<input type="hidden" name="choices-TOTAL_FORMS" value="1" /><input type="hidden" name="choices-INITIAL_FORMS" value="0" /><input type="hidden" name="choices-MIN_NUM_FORMS" value="0" /><input type="hidden" name="choices-MAX_NUM_FORMS" value="1000" />
96
96
<tr><th>Choice:</th><td><input type="text" name="choices-0-choice" /></td></tr>
97
97
<tr><th>Votes:</th><td><input type="number" name="choices-0-votes" /></td></tr>""")
98
98
 
198
198
        # number of forms to be completed.
199
199
 
200
200
        data = {
201
 
            'choices-TOTAL_FORMS': '3', # the number of forms rendered
202
 
            'choices-INITIAL_FORMS': '0', # the number of forms with initial data
203
 
            'choices-MAX_NUM_FORMS': '0', # max number of forms
 
201
            'choices-TOTAL_FORMS': '3',  # the number of forms rendered
 
202
            'choices-INITIAL_FORMS': '0',  # the number of forms with initial data
 
203
            'choices-MIN_NUM_FORMS': '0',  # min number of forms
 
204
            'choices-MAX_NUM_FORMS': '0',  # max number of forms
204
205
            'choices-0-choice': '',
205
206
            'choices-0-votes': '',
206
207
            'choices-1-choice': '',
213
214
        self.assertTrue(formset.is_valid())
214
215
        self.assertEqual([form.cleaned_data for form in formset.forms], [{}, {}, {}])
215
216
 
 
217
    def test_min_num_displaying_more_than_one_blank_form(self):
 
218
        # We can also display more than 1 empty form passing min_num argument
 
219
        # to formset_factory. It will (essentially) increment the extra argument
 
220
        ChoiceFormSet = formset_factory(Choice, extra=1, min_num=1)
 
221
 
 
222
        formset = ChoiceFormSet(auto_id=False, prefix='choices')
 
223
        form_output = []
 
224
 
 
225
        for form in formset.forms:
 
226
            form_output.append(form.as_ul())
 
227
 
 
228
        # Min_num forms are required; extra forms can be empty.
 
229
        self.assertFalse(formset.forms[0].empty_permitted)
 
230
        self.assertTrue(formset.forms[1].empty_permitted)
 
231
 
 
232
        self.assertHTMLEqual('\n'.join(form_output), """<li>Choice: <input type="text" name="choices-0-choice" /></li>
 
233
<li>Votes: <input type="number" name="choices-0-votes" /></li>
 
234
<li>Choice: <input type="text" name="choices-1-choice" /></li>
 
235
<li>Votes: <input type="number" name="choices-1-votes" /></li>""")
 
236
 
 
237
    def test_min_num_displaying_more_than_one_blank_form_with_zero_extra(self):
 
238
        # We can also display more than 1 empty form passing min_num argument
 
239
        ChoiceFormSet = formset_factory(Choice, extra=0, min_num=3)
 
240
 
 
241
        formset = ChoiceFormSet(auto_id=False, prefix='choices')
 
242
        form_output = []
 
243
 
 
244
        for form in formset.forms:
 
245
            form_output.append(form.as_ul())
 
246
 
 
247
        self.assertHTMLEqual('\n'.join(form_output), """<li>Choice: <input type="text" name="choices-0-choice" /></li>
 
248
<li>Votes: <input type="number" name="choices-0-votes" /></li>
 
249
<li>Choice: <input type="text" name="choices-1-choice" /></li>
 
250
<li>Votes: <input type="number" name="choices-1-votes" /></li>
 
251
<li>Choice: <input type="text" name="choices-2-choice" /></li>
 
252
<li>Votes: <input type="number" name="choices-2-votes" /></li>""")
 
253
 
216
254
    def test_single_form_completed(self):
217
255
        # We can just fill out one of the forms.
218
256
 
219
257
        data = {
220
 
            'choices-TOTAL_FORMS': '3', # the number of forms rendered
221
 
            'choices-INITIAL_FORMS': '0', # the number of forms with initial data
222
 
            'choices-MAX_NUM_FORMS': '0', # max number of forms
 
258
            'choices-TOTAL_FORMS': '3',  # the number of forms rendered
 
259
            'choices-INITIAL_FORMS': '0',  # the number of forms with initial data
 
260
            'choices-MIN_NUM_FORMS': '0',  # min number of forms
 
261
            'choices-MAX_NUM_FORMS': '0',  # max number of forms
223
262
            'choices-0-choice': 'Calexico',
224
263
            'choices-0-votes': '100',
225
264
            'choices-1-choice': '',
240
279
        # value in the returned data is not checked)
241
280
 
242
281
        data = {
243
 
            'choices-TOTAL_FORMS': '2', # the number of forms rendered
244
 
            'choices-INITIAL_FORMS': '0', # the number of forms with initial data
245
 
            'choices-MAX_NUM_FORMS': '2', # max number of forms - should be ignored
 
282
            'choices-TOTAL_FORMS': '2',  # the number of forms rendered
 
283
            'choices-INITIAL_FORMS': '0',  # the number of forms with initial data
 
284
            'choices-MIN_NUM_FORMS': '0',  # min number of forms
 
285
            'choices-MAX_NUM_FORMS': '2',  # max number of forms - should be ignored
246
286
            'choices-0-choice': 'Zero',
247
287
            'choices-0-votes': '0',
248
288
            'choices-1-choice': 'One',
254
294
        self.assertFalse(formset.is_valid())
255
295
        self.assertEqual(formset.non_form_errors(), ['Please submit 1 or fewer forms.'])
256
296
 
 
297
    def test_formset_validate_min_flag(self):
 
298
        # If validate_min is set and min_num is more than TOTAL_FORMS in the
 
299
        # data, then throw an exception. MIN_NUM_FORMS in the data is
 
300
        # irrelevant here (it's output as a hint for the client but its
 
301
        # value in the returned data is not checked)
 
302
 
 
303
        data = {
 
304
            'choices-TOTAL_FORMS': '2',  # the number of forms rendered
 
305
            'choices-INITIAL_FORMS': '0',  # the number of forms with initial data
 
306
            'choices-MIN_NUM_FORMS': '0',  # min number of forms
 
307
            'choices-MAX_NUM_FORMS': '0',  # max number of forms - should be ignored
 
308
            'choices-0-choice': 'Zero',
 
309
            'choices-0-votes': '0',
 
310
            'choices-1-choice': 'One',
 
311
            'choices-1-votes': '1',
 
312
        }
 
313
 
 
314
        ChoiceFormSet = formset_factory(Choice, extra=1, min_num=3, validate_min=True)
 
315
        formset = ChoiceFormSet(data, auto_id=False, prefix='choices')
 
316
        self.assertFalse(formset.is_valid())
 
317
        self.assertEqual(formset.non_form_errors(), ['Please submit 3 or more forms.'])
 
318
 
257
319
    def test_second_form_partially_filled_2(self):
258
320
        # And once again, if we try to partially complete a form, validation will fail.
259
321
 
260
322
        data = {
261
 
            'choices-TOTAL_FORMS': '3', # the number of forms rendered
262
 
            'choices-INITIAL_FORMS': '0', # the number of forms with initial data
263
 
            'choices-MAX_NUM_FORMS': '0', # max number of forms
 
323
            'choices-TOTAL_FORMS': '3',  # the number of forms rendered
 
324
            'choices-INITIAL_FORMS': '0',  # the number of forms with initial data
 
325
            'choices-MIN_NUM_FORMS': '0',  # min number of forms
 
326
            'choices-MAX_NUM_FORMS': '0',  # max number of forms
264
327
            'choices-0-choice': 'Calexico',
265
328
            'choices-0-votes': '100',
266
329
            'choices-1-choice': 'The Decemberists',
267
 
            'choices-1-votes': '', # missing value
 
330
            'choices-1-votes': '',  # missing value
268
331
            'choices-2-choice': '',
269
332
            'choices-2-votes': '',
270
333
        }
277
340
    def test_more_initial_data(self):
278
341
        # The extra argument also works when the formset is pre-filled with initial
279
342
        # data.
280
 
 
281
 
        data = {
282
 
            'choices-TOTAL_FORMS': '3', # the number of forms rendered
283
 
            'choices-INITIAL_FORMS': '0', # the number of forms with initial data
284
 
            'choices-MAX_NUM_FORMS': '0', # max number of forms
285
 
            'choices-0-choice': 'Calexico',
286
 
            'choices-0-votes': '100',
287
 
            'choices-1-choice': '',
288
 
            'choices-1-votes': '', # missing value
289
 
            'choices-2-choice': '',
290
 
            'choices-2-votes': '',
291
 
        }
292
 
 
293
343
        initial = [{'choice': 'Calexico', 'votes': 100}]
294
344
        ChoiceFormSet = formset_factory(Choice, extra=3)
295
345
        formset = ChoiceFormSet(initial=initial, auto_id=False, prefix='choices')
342
392
        # 'on'. Let's go ahead and delete Fergie.
343
393
 
344
394
        data = {
345
 
            'choices-TOTAL_FORMS': '3', # the number of forms rendered
346
 
            'choices-INITIAL_FORMS': '2', # the number of forms with initial data
347
 
            'choices-MAX_NUM_FORMS': '0', # max number of forms
 
395
            'choices-TOTAL_FORMS': '3',  # the number of forms rendered
 
396
            'choices-INITIAL_FORMS': '2',  # the number of forms with initial data
 
397
            'choices-MIN_NUM_FORMS': '0',  # min number of forms
 
398
            'choices-MAX_NUM_FORMS': '0',  # max number of forms
348
399
            'choices-0-choice': 'Calexico',
349
400
            'choices-0-votes': '100',
350
401
            'choices-0-DELETE': '',
366
417
        # it's going to be deleted.
367
418
 
368
419
        class CheckForm(Form):
369
 
           field = IntegerField(min_value=100)
 
420
            field = IntegerField(min_value=100)
370
421
 
371
422
        data = {
372
 
            'check-TOTAL_FORMS': '3', # the number of forms rendered
373
 
            'check-INITIAL_FORMS': '2', # the number of forms with initial data
374
 
            'check-MAX_NUM_FORMS': '0', # max number of forms
 
423
            'check-TOTAL_FORMS': '3',  # the number of forms rendered
 
424
            'check-INITIAL_FORMS': '2',  # the number of forms with initial data
 
425
            'choices-MIN_NUM_FORMS': '0',  # min number of forms
 
426
            'check-MAX_NUM_FORMS': '0',  # max number of forms
375
427
            'check-0-field': '200',
376
428
            'check-0-DELETE': '',
377
429
            'check-1-field': '50',
399
451
            can_delete=True)
400
452
 
401
453
        p = PeopleForm(
402
 
            {'form-0-name': '', 'form-0-DELETE': 'on', # no name!
 
454
            {'form-0-name': '', 'form-0-DELETE': 'on',  # no name!
403
455
             'form-TOTAL_FORMS': 1, 'form-INITIAL_FORMS': 1,
404
 
             'form-MAX_NUM_FORMS': 1})
 
456
             'form-MIN_NUM_FORMS': 0, 'form-MAX_NUM_FORMS': 1})
405
457
 
406
458
        self.assertTrue(p.is_valid())
407
459
        self.assertEqual(len(p.deleted_forms), 1)
409
461
    def test_formsets_with_ordering(self):
410
462
        # FormSets with ordering ######################################################
411
463
        # We can also add ordering ability to a FormSet with an argument to
412
 
        # formset_factory. This will add a integer field to each form instance. When
 
464
        # formset_factory. This will add an integer field to each form instance. When
413
465
        # form validation succeeds, [form.cleaned_data for form in formset.forms] will have the data in the correct
414
466
        # order specified by the ordering fields. If a number is duplicated in the set
415
467
        # of ordering fields, for instance form 0 and form 3 are both marked as 1, then
436
488
<li>Order: <input type="number" name="choices-2-ORDER" /></li>""")
437
489
 
438
490
        data = {
439
 
            'choices-TOTAL_FORMS': '3', # the number of forms rendered
440
 
            'choices-INITIAL_FORMS': '2', # the number of forms with initial data
441
 
            'choices-MAX_NUM_FORMS': '0', # max number of forms
 
491
            'choices-TOTAL_FORMS': '3',  # the number of forms rendered
 
492
            'choices-INITIAL_FORMS': '2',  # the number of forms with initial data
 
493
            'choices-MIN_NUM_FORMS': '0',  # min number of forms
 
494
            'choices-MAX_NUM_FORMS': '0',  # max number of forms
442
495
            'choices-0-choice': 'Calexico',
443
496
            'choices-0-votes': '100',
444
497
            'choices-0-ORDER': '1',
468
521
        # they will be sorted below everything else.
469
522
 
470
523
        data = {
471
 
            'choices-TOTAL_FORMS': '4', # the number of forms rendered
472
 
            'choices-INITIAL_FORMS': '3', # the number of forms with initial data
473
 
            'choices-MAX_NUM_FORMS': '0', # max number of forms
 
524
            'choices-TOTAL_FORMS': '4',  # the number of forms rendered
 
525
            'choices-INITIAL_FORMS': '3',  # the number of forms with initial data
 
526
            'choices-MIN_NUM_FORMS': '0',  # min number of forms
 
527
            'choices-MAX_NUM_FORMS': '0',  # max number of forms
474
528
            'choices-0-choice': 'Calexico',
475
529
            'choices-0-votes': '100',
476
530
            'choices-0-ORDER': '1',
504
558
        # Ordering should work with blank fieldsets.
505
559
 
506
560
        data = {
507
 
            'choices-TOTAL_FORMS': '3', # the number of forms rendered
508
 
            'choices-INITIAL_FORMS': '0', # the number of forms with initial data
509
 
            'choices-MAX_NUM_FORMS': '0', # max number of forms
 
561
            'choices-TOTAL_FORMS': '3',  # the number of forms rendered
 
562
            'choices-INITIAL_FORMS': '0',  # the number of forms with initial data
 
563
            'choices-MIN_NUM_FORMS': '0',  # min number of forms
 
564
            'choices-MAX_NUM_FORMS': '0',  # max number of forms
510
565
        }
511
566
 
512
567
        ChoiceFormSet = formset_factory(Choice, can_order=True)
556
611
        # Let's delete Fergie, and put The Decemberists ahead of Calexico.
557
612
 
558
613
        data = {
559
 
            'choices-TOTAL_FORMS': '4', # the number of forms rendered
560
 
            'choices-INITIAL_FORMS': '3', # the number of forms with initial data
561
 
            'choices-MAX_NUM_FORMS': '0', # max number of forms
 
614
            'choices-TOTAL_FORMS': '4',  # the number of forms rendered
 
615
            'choices-INITIAL_FORMS': '3',  # the number of forms with initial data
 
616
            'choices-MIN_NUM_FORMS': '0',  # min number of forms
 
617
            'choices-MAX_NUM_FORMS': '0',  # max number of forms
562
618
            'choices-0-choice': 'Calexico',
563
619
            'choices-0-votes': '100',
564
620
            'choices-0-ORDER': '1',
601
657
 
602
658
        p = PeopleForm({
603
659
            'form-0-name': '',
604
 
            'form-0-DELETE': 'on', # no name!
 
660
            'form-0-DELETE': 'on',  # no name!
605
661
            'form-TOTAL_FORMS': 1,
606
662
            'form-INITIAL_FORMS': 1,
 
663
            'form-MIN_NUM_FORMS': 0,
607
664
            'form-MAX_NUM_FORMS': 1
608
665
        })
609
666
 
618
675
        # We start out with a some duplicate data.
619
676
 
620
677
        data = {
621
 
            'drinks-TOTAL_FORMS': '2', # the number of forms rendered
622
 
            'drinks-INITIAL_FORMS': '0', # the number of forms with initial data
623
 
            'drinks-MAX_NUM_FORMS': '0', # max number of forms
 
678
            'drinks-TOTAL_FORMS': '2',  # the number of forms rendered
 
679
            'drinks-INITIAL_FORMS': '0',  # the number of forms with initial data
 
680
            'drinks-MIN_NUM_FORMS': '0',  # min number of forms
 
681
            'drinks-MAX_NUM_FORMS': '0',  # max number of forms
624
682
            'drinks-0-name': 'Gin and Tonic',
625
683
            'drinks-1-name': 'Gin and Tonic',
626
684
        }
637
695
        # Make sure we didn't break the valid case.
638
696
 
639
697
        data = {
640
 
            'drinks-TOTAL_FORMS': '2', # the number of forms rendered
641
 
            'drinks-INITIAL_FORMS': '0', # the number of forms with initial data
642
 
            'drinks-MAX_NUM_FORMS': '0', # max number of forms
 
698
            'drinks-TOTAL_FORMS': '2',  # the number of forms rendered
 
699
            'drinks-INITIAL_FORMS': '0',  # the number of forms with initial data
 
700
            'drinks-MIN_NUM_FORMS': '0',  # min number of forms
 
701
            'drinks-MAX_NUM_FORMS': '0',  # max number of forms
643
702
            'drinks-0-name': 'Gin and Tonic',
644
703
            'drinks-1-name': 'Bloody Mary',
645
704
        }
791
850
        data = {
792
851
            'form-TOTAL_FORMS': '2',
793
852
            'form-INITIAL_FORMS': '0',
 
853
            'form-MIN_NUM_FORMS': '0',
794
854
            'form-MAX_NUM_FORMS': '0',
795
855
        }
796
856
        formset = FavoriteDrinksFormSet(data=data)
803
863
        # Regression test for #12878 #################################################
804
864
 
805
865
        data = {
806
 
            'drinks-TOTAL_FORMS': '2', # the number of forms rendered
807
 
            'drinks-INITIAL_FORMS': '0', # the number of forms with initial data
808
 
            'drinks-MAX_NUM_FORMS': '0', # max number of forms
 
866
            'drinks-TOTAL_FORMS': '2',  # the number of forms rendered
 
867
            'drinks-INITIAL_FORMS': '0',  # the number of forms with initial data
 
868
            'drinks-MIN_NUM_FORMS': '0',  # min number of forms
 
869
            'drinks-MAX_NUM_FORMS': '0',  # max number of forms
809
870
            'drinks-0-name': 'Gin and Tonic',
810
871
            'drinks-1-name': 'Gin and Tonic',
811
872
        }
894
955
        data = {
895
956
            'choices-TOTAL_FORMS': '1',  # number of forms rendered
896
957
            'choices-INITIAL_FORMS': '0',  # number of forms with initial data
 
958
            'choices-MIN_NUM_FORMS': '0',  # min number of forms
897
959
            'choices-MAX_NUM_FORMS': '0',  # max number of forms
898
960
            'choices-0-choice': 'Calexico',
899
961
            'choices-0-votes': '100',
900
962
        }
901
963
        formset = AnotherChoiceFormSet(data, auto_id=False, prefix='choices')
902
964
        self.assertTrue(formset.is_valid())
903
 
        self.assertTrue(all([form.is_valid_called for form in formset.forms]))
 
965
        self.assertTrue(all(form.is_valid_called for form in formset.forms))
904
966
 
905
967
    def test_hard_limit_on_instantiated_forms(self):
906
968
        """A formset has a hard limit on the number of forms instantiated."""
914
976
                {
915
977
                    'choices-TOTAL_FORMS': '4',
916
978
                    'choices-INITIAL_FORMS': '0',
 
979
                    'choices-MIN_NUM_FORMS': '0',  # min number of forms
917
980
                    'choices-MAX_NUM_FORMS': '4',
918
981
                    'choices-0-choice': 'Zero',
919
982
                    'choices-0-votes': '0',
923
986
                    'choices-2-votes': '2',
924
987
                    'choices-3-choice': 'Three',
925
988
                    'choices-3-votes': '3',
926
 
                    },
 
989
                },
927
990
                prefix='choices',
928
 
                )
 
991
            )
929
992
            # But we still only instantiate 3 forms
930
993
            self.assertEqual(len(formset.forms), 3)
931
994
            # and the formset isn't valid
945
1008
                {
946
1009
                    'choices-TOTAL_FORMS': '4',
947
1010
                    'choices-INITIAL_FORMS': '0',
 
1011
                    'choices-MIN_NUM_FORMS': '0',  # min number of forms
948
1012
                    'choices-MAX_NUM_FORMS': '4',
949
1013
                    'choices-0-choice': 'Zero',
950
1014
                    'choices-0-votes': '0',
954
1018
                    'choices-2-votes': '2',
955
1019
                    'choices-3-choice': 'Three',
956
1020
                    'choices-3-votes': '3',
957
 
                    },
 
1021
                },
958
1022
                prefix='choices',
959
 
                )
 
1023
            )
960
1024
            # Four forms are instantiated and no exception is raised
961
1025
            self.assertEqual(len(formset.forms), 4)
962
1026
        finally:
978
1042
 
979
1043
    def test_validate_max_ignores_forms_marked_for_deletion(self):
980
1044
        class CheckForm(Form):
981
 
           field = IntegerField()
 
1045
            field = IntegerField()
982
1046
 
983
1047
        data = {
984
1048
            'check-TOTAL_FORMS': '2',
994
1058
        formset = CheckFormSet(data, prefix='check')
995
1059
        self.assertTrue(formset.is_valid())
996
1060
 
997
 
 
998
1061
    def test_formset_total_error_count(self):
999
1062
        """A valid formset should have 0 total errors."""
1000
 
        data = [ #  formset_data, expected error count
 
1063
        data = [  # formset_data, expected error count
1001
1064
            ([('Calexico', '100')], 0),
1002
1065
            ([('Calexico', '')], 1),
1003
1066
            ([('', 'invalid')], 2),
1004
1067
            ([('Calexico', '100'), ('Calexico', '')], 1),
1005
1068
            ([('Calexico', ''), ('Calexico', '')], 2),
1006
1069
        ]
1007
 
        
 
1070
 
1008
1071
        for formset_data, expected_error_count in data:
1009
1072
            formset = self.make_choiceformset(formset_data)
1010
1073
            self.assertEqual(formset.total_error_count(), expected_error_count)
1011
1074
 
1012
1075
    def test_formset_total_error_count_with_non_form_errors(self):
1013
1076
        data = {
1014
 
            'choices-TOTAL_FORMS': '2', # the number of forms rendered
1015
 
            'choices-INITIAL_FORMS': '0', # the number of forms with initial data
1016
 
            'choices-MAX_NUM_FORMS': '2', # max number of forms - should be ignored
 
1077
            'choices-TOTAL_FORMS': '2',  # the number of forms rendered
 
1078
            'choices-INITIAL_FORMS': '0',  # the number of forms with initial data
 
1079
            'choices-MAX_NUM_FORMS': '2',  # max number of forms - should be ignored
1017
1080
            'choices-0-choice': 'Zero',
1018
1081
            'choices-0-votes': '0',
1019
1082
            'choices-1-choice': 'One',
1030
1093
 
1031
1094
 
1032
1095
data = {
1033
 
    'choices-TOTAL_FORMS': '1', # the number of forms rendered
1034
 
    'choices-INITIAL_FORMS': '0', # the number of forms with initial data
1035
 
    'choices-MAX_NUM_FORMS': '0', # max number of forms
 
1096
    'choices-TOTAL_FORMS': '1',  # the number of forms rendered
 
1097
    'choices-INITIAL_FORMS': '0',  # the number of forms with initial data
 
1098
    'choices-MIN_NUM_FORMS': '0',  # min number of forms
 
1099
    'choices-MAX_NUM_FORMS': '0',  # max number of forms
1036
1100
    'choices-0-choice': 'Calexico',
1037
1101
    'choices-0-votes': '100',
1038
1102
}
1039
1103
 
 
1104
 
1040
1105
class Choice(Form):
1041
1106
    choice = CharField()
1042
1107
    votes = IntegerField()
1043
1108
 
1044
1109
ChoiceFormSet = formset_factory(Choice)
1045
1110
 
 
1111
 
1046
1112
class FormsetAsFooTests(TestCase):
1047
1113
    def test_as_table(self):
1048
1114
        formset = ChoiceFormSet(data, auto_id=False, prefix='choices')
1049
 
        self.assertHTMLEqual(formset.as_table(),"""<input type="hidden" name="choices-TOTAL_FORMS" value="1" /><input type="hidden" name="choices-INITIAL_FORMS" value="0" /><input type="hidden" name="choices-MAX_NUM_FORMS" value="0" />
 
1115
        self.assertHTMLEqual(formset.as_table(), """<input type="hidden" name="choices-TOTAL_FORMS" value="1" /><input type="hidden" name="choices-INITIAL_FORMS" value="0" /><input type="hidden" name="choices-MIN_NUM_FORMS" value="0" /><input type="hidden" name="choices-MAX_NUM_FORMS" value="0" />
1050
1116
<tr><th>Choice:</th><td><input type="text" name="choices-0-choice" value="Calexico" /></td></tr>
1051
1117
<tr><th>Votes:</th><td><input type="number" name="choices-0-votes" value="100" /></td></tr>""")
1052
1118
 
1053
1119
    def test_as_p(self):
1054
1120
        formset = ChoiceFormSet(data, auto_id=False, prefix='choices')
1055
 
        self.assertHTMLEqual(formset.as_p(),"""<input type="hidden" name="choices-TOTAL_FORMS" value="1" /><input type="hidden" name="choices-INITIAL_FORMS" value="0" /><input type="hidden" name="choices-MAX_NUM_FORMS" value="0" />
 
1121
        self.assertHTMLEqual(formset.as_p(), """<input type="hidden" name="choices-TOTAL_FORMS" value="1" /><input type="hidden" name="choices-INITIAL_FORMS" value="0" /><input type="hidden" name="choices-MIN_NUM_FORMS" value="0" /><input type="hidden" name="choices-MAX_NUM_FORMS" value="0" />
1056
1122
<p>Choice: <input type="text" name="choices-0-choice" value="Calexico" /></p>
1057
1123
<p>Votes: <input type="number" name="choices-0-votes" value="100" /></p>""")
1058
1124
 
1059
1125
    def test_as_ul(self):
1060
1126
        formset = ChoiceFormSet(data, auto_id=False, prefix='choices')
1061
 
        self.assertHTMLEqual(formset.as_ul(),"""<input type="hidden" name="choices-TOTAL_FORMS" value="1" /><input type="hidden" name="choices-INITIAL_FORMS" value="0" /><input type="hidden" name="choices-MAX_NUM_FORMS" value="0" />
 
1127
        self.assertHTMLEqual(formset.as_ul(), """<input type="hidden" name="choices-TOTAL_FORMS" value="1" /><input type="hidden" name="choices-INITIAL_FORMS" value="0" /><input type="hidden" name="choices-MIN_NUM_FORMS" value="0" /><input type="hidden" name="choices-MAX_NUM_FORMS" value="0" />
1062
1128
<li>Choice: <input type="text" name="choices-0-choice" value="Calexico" /></li>
1063
1129
<li>Votes: <input type="number" name="choices-0-votes" value="100" /></li>""")
1064
1130
 
1070
1136
 
1071
1137
ArticleFormSet = formset_factory(ArticleForm)
1072
1138
 
 
1139
 
1073
1140
class TestIsBoundBehavior(TestCase):
1074
1141
    def test_no_data_raises_validation_error(self):
1075
1142
        with self.assertRaises(ValidationError):
1089
1156
        self.assertTrue(formset.forms[0].is_valid())
1090
1157
        self.assertEqual([{}], formset.cleaned_data)
1091
1158
 
1092
 
 
1093
1159
    def test_form_errors_are_caught_by_formset(self):
1094
1160
        data = {
1095
1161
            'form-TOTAL_FORMS': '2',
1097
1163
            'form-0-title': 'Test',
1098
1164
            'form-0-pub_date': '1904-06-16',
1099
1165
            'form-1-title': 'Test',
1100
 
            'form-1-pub_date': '', # <-- this date is missing but required
 
1166
            'form-1-pub_date': '',  # <-- this date is missing but required
1101
1167
        }
1102
1168
        formset = ArticleFormSet(data)
1103
1169
        self.assertFalse(formset.is_valid())
1125
1191
        # The empty forms should be equal.
1126
1192
        self.assertHTMLEqual(empty_forms[0].as_p(), empty_forms[1].as_p())
1127
1193
 
 
1194
 
1128
1195
class TestEmptyFormSet(TestCase):
1129
1196
    def test_empty_formset_is_valid(self):
1130
1197
        """Test that an empty formset still calls clean()"""
1131
1198
        EmptyFsetWontValidateFormset = formset_factory(FavoriteDrinkForm, extra=0, formset=EmptyFsetWontValidate)
1132
 
        formset = EmptyFsetWontValidateFormset(data={'form-INITIAL_FORMS':'0', 'form-TOTAL_FORMS':'0'},prefix="form")
1133
 
        formset2 = EmptyFsetWontValidateFormset(data={'form-INITIAL_FORMS':'0', 'form-TOTAL_FORMS':'1', 'form-0-name':'bah' },prefix="form")
 
1199
        formset = EmptyFsetWontValidateFormset(data={'form-INITIAL_FORMS': '0', 'form-TOTAL_FORMS': '0'}, prefix="form")
 
1200
        formset2 = EmptyFsetWontValidateFormset(data={'form-INITIAL_FORMS': '0', 'form-TOTAL_FORMS': '1', 'form-0-name': 'bah'}, prefix="form")
1134
1201
        self.assertFalse(formset.is_valid())
1135
1202
        self.assertFalse(formset2.is_valid())
1136
1203