Ticket #9532: 9532.patch
File 9532.patch, 11.7 KB (added by , 13 years ago) |
---|
-
django/forms/formsets.py
diff --git a/django/forms/formsets.py b/django/forms/formsets.py index dcd2f01..0c53006 100644
a b __all__ = ('BaseFormSet', 'all_valid') 16 16 TOTAL_FORM_COUNT = 'TOTAL_FORMS' 17 17 INITIAL_FORM_COUNT = 'INITIAL_FORMS' 18 18 MAX_NUM_FORM_COUNT = 'MAX_NUM_FORMS' 19 MIN_NUM_FORM_COUNT = 'MIN_NUM_FORMS' 19 20 ORDERING_FIELD_NAME = 'ORDER' 20 21 DELETION_FIELD_NAME = 'DELETE' 21 22 … … class ManagementForm(Form): 29 30 self.base_fields[TOTAL_FORM_COUNT] = IntegerField(widget=HiddenInput) 30 31 self.base_fields[INITIAL_FORM_COUNT] = IntegerField(widget=HiddenInput) 31 32 self.base_fields[MAX_NUM_FORM_COUNT] = IntegerField(required=False, widget=HiddenInput) 33 self.base_fields[MIN_NUM_FORM_COUNT] = IntegerField(required=False, widget=HiddenInput) 32 34 super(ManagementForm, self).__init__(*args, **kwargs) 33 35 34 36 class BaseFormSet(StrAndUnicode): … … class BaseFormSet(StrAndUnicode): 77 79 form = ManagementForm(auto_id=self.auto_id, prefix=self.prefix, initial={ 78 80 TOTAL_FORM_COUNT: self.total_form_count(), 79 81 INITIAL_FORM_COUNT: self.initial_form_count(), 80 MAX_NUM_FORM_COUNT: self.max_num 82 MAX_NUM_FORM_COUNT: self.max_num, 83 MIN_NUM_FORM_COUNT: self.min_num 81 84 }) 82 85 return form 83 86 management_form = property(_management_form) … … class BaseFormSet(StrAndUnicode): 95 98 total_forms = initial_forms 96 99 elif total_forms > self.max_num >= 0: 97 100 total_forms = self.max_num 101 102 total_forms = max(total_forms, self.min_num) 103 98 104 return total_forms 99 105 100 106 def initial_form_count(self): … … class BaseFormSet(StrAndUnicode): 358 364 return mark_safe(u'\n'.join([unicode(self.management_form), forms])) 359 365 360 366 def formset_factory(form, formset=BaseFormSet, extra=1, can_order=False, 361 can_delete=False, max_num=None ):367 can_delete=False, max_num=None, min_num=None): 362 368 """Return a FormSet for the given form class.""" 369 if not None in (max_num, min_num) and max_num < min_num: 370 raise ValueError('max_num cannot be smaller than min_num') 371 363 372 attrs = {'form': form, 'extra': extra, 364 373 'can_order': can_order, 'can_delete': can_delete, 365 'max_num': max_num }374 'max_num': max_num, 'min_num': min_num } 366 375 return type(form.__name__ + 'FormSet', (formset,), attrs) 367 376 377 368 378 def all_valid(formsets): 369 379 """Returns true if every formset in formsets is valid.""" 370 380 valid = True -
docs/topics/forms/formsets.txt
diff --git a/docs/topics/forms/formsets.txt b/docs/topics/forms/formsets.txt index b524c24..9a91182 100644
a b A ``max_num`` value of ``None`` (the default) puts no limit on the number of 112 112 forms displayed. Please note that the default value of ``max_num`` was changed 113 113 from ``0`` to ``None`` in version 1.2 to allow ``0`` as a valid value. 114 114 115 .. _formsets-min-num: 116 117 Ensuring the minimum number of forms 118 ------------------------------------ 119 120 The ``min_num`` parameter to ``formset_factory`` gives you the ability to make 121 sure that at least a certain number of forms will be displayed:: 122 123 >>> ArticleFormSet = formset_factory(ArticleForm, min_num=2) 124 >>> formset = ArticleFormset(initial=[ 125 ... {'title': u'Django is now open source', 126 ... 'pub_date': datetime.date.today(),} 127 ... ]) 128 129 >>> for form in formset: 130 ... print form.as_table() 131 <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> 132 <tr><th><label for="id_form-0-pub_date">Pub date:</label></th><td><input type="text" name="form-0-pub_date" value="2012-04-02" id="id_form-0-pub_date" /></td></tr> 133 <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> 134 <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> 135 136 .. versionchanged:: 1.4 137 138 If the value of ``min_num`` is greater than the number of elements that would 139 be displayed normally (including extra forms), additional blank forms will be 140 added to the formset. 141 142 If ``min_num`` is greater than ``max_num``, ``ValueError`` will be raised by 143 ``formset_factory`` function. 144 115 145 Formset validation 116 146 ------------------ 117 147 -
tests/regressiontests/forms/tests/formsets.py
diff --git a/tests/regressiontests/forms/tests/formsets.py b/tests/regressiontests/forms/tests/formsets.py index 05ef978..957f04b 100644
a b class FormsFormsetTestCase(TestCase): 47 47 # for adding data. By default, it displays 1 blank form. It can display more, 48 48 # but we'll look at how to do so later. 49 49 formset = ChoiceFormSet(auto_id=False, prefix='choices') 50 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" /> 50 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" /><input type="hidden" name="choices-MIN_NUM_FORMS" /> 51 51 <tr><th>Choice:</th><td><input type="text" name="choices-0-choice" /></td></tr> 52 52 <tr><th>Votes:</th><td><input type="text" name="choices-0-votes" /></td></tr>""") 53 53 … … class FormsFormsetTestCase(TestCase): 646 646 self.assertTrue(formset.is_valid()) 647 647 self.assertEqual(formset.non_form_errors(), []) 648 648 649 def test_appending_min_forms(self): 650 # Ensuring the minimum number of forms ######################################## 651 # Base case for min_num. 652 653 # When not passed, min_num will take its default value of None, i.e. no required 654 # forms, only controller by the value of the extra parameter. 655 656 initial = [ dict(name='Pony') ] 657 LimitedFavoriteDrinkFormSet = formset_factory(FavoriteDrinkForm, extra=0, min_num=1) 658 formset = LimitedFavoriteDrinkFormSet() 659 660 self.assertHTMLEqual('\n'.join(str(form) for form in formset.forms), """<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>""") 661 662 LimitedFavoriteDrinkFormSet = formset_factory(FavoriteDrinkForm, extra=0, min_num=2) 663 formset = LimitedFavoriteDrinkFormSet(initial=initial) 664 665 self.assertHTMLEqual('\n'.join(str(form) for form in formset.forms), """<tr><th><label for="id_form-0-name">Name:</label></th><td><input type="text" name="form-0-name" id="id_form-0-name" value="Pony" /></td></tr> 666 <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>""") 667 668 LimitedFavoriteDrinkFormSet = formset_factory(FavoriteDrinkForm, extra=0, min_num=1) 669 formset = LimitedFavoriteDrinkFormSet(initial=initial) 670 671 self.assertHTMLEqual('\n'.join(str(form) for form in formset.forms), """<tr><th><label for="id_form-0-name">Name:</label></th><td><input type="text" name="form-0-name" id="id_form-0-name" value="Pony" /></td></tr>""") 672 673 LimitedFavoriteDrinkFormSet = formset_factory(FavoriteDrinkForm, extra=1, min_num=1) 674 formset = LimitedFavoriteDrinkFormSet(initial=initial) 675 676 self.assertHTMLEqual('\n'.join(str(form) for form in formset.forms), """<tr><th><label for="id_form-0-name">Name:</label></th><td><input type="text" name="form-0-name" id="id_form-0-name" value="Pony" /></td></tr> 677 <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>""") 678 679 LimitedFavoriteDrinkFormSet = formset_factory(FavoriteDrinkForm, extra=1, min_num=1, max_num=1) 680 formset = LimitedFavoriteDrinkFormSet(initial=initial) 681 682 self.assertHTMLEqual('\n'.join(str(form) for form in formset.forms), """<tr><th><label for="id_form-0-name">Name:</label></th><td><input type="text" name="form-0-name" id="id_form-0-name" value="Pony" /></td></tr>""") 683 684 def test_formset_management_form_contains_min_num(self): 685 self.maxDiff = None 686 LimitedFavoriteDrinkFormSet = formset_factory(FavoriteDrinkForm, extra=0, min_num=1) 687 formset = LimitedFavoriteDrinkFormSet() 688 689 self.assertHTMLEqual(str(formset), """<input type="hidden" id="id_form-TOTAL_FORMS" name="form-TOTAL_FORMS" value="1" /><input type="hidden" id="id_form-INITIAL_FORMS" name="form-INITIAL_FORMS" value="0" /><input type="hidden" id="id_form-MAX_NUM_FORMS" name="form-MAX_NUM_FORMS" /><input type="hidden" id="id_form-MIN_NUM_FORMS" name="form-MIN_NUM_FORMS" value="1" /> 690 <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>""") 691 692 def test_wrong_min_forms(self): 693 self.assertRaises(ValueError, formset_factory, FavoriteDrinkForm, min_num=2, max_num=1) 694 649 695 def test_limiting_max_forms(self): 650 696 # Limiting the maximum number of forms ######################################## 651 697 # Base case for max_num. … … ChoiceFormSet = formset_factory(Choice) 862 908 class FormsetAsFooTests(TestCase): 863 909 def test_as_table(self): 864 910 formset = ChoiceFormSet(data, auto_id=False, prefix='choices') 865 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" /> 911 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" /><input type="hidden" name="choices-MIN_NUM_FORMS" /> 866 912 <tr><th>Choice:</th><td><input type="text" name="choices-0-choice" value="Calexico" /></td></tr> 867 913 <tr><th>Votes:</th><td><input type="text" name="choices-0-votes" value="100" /></td></tr>""") 868 914 869 915 def test_as_p(self): 870 916 formset = ChoiceFormSet(data, auto_id=False, prefix='choices') 871 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" /> 917 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" /><input type="hidden" name="choices-MIN_NUM_FORMS" /> 872 918 <p>Choice: <input type="text" name="choices-0-choice" value="Calexico" /></p> 873 919 <p>Votes: <input type="text" name="choices-0-votes" value="100" /></p>""") 874 920 875 921 def test_as_ul(self): 876 922 formset = ChoiceFormSet(data, auto_id=False, prefix='choices') 877 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" /> 923 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" /><input type="hidden" name="choices-MIN_NUM_FORMS" /> 878 924 <li>Choice: <input type="text" name="choices-0-choice" value="Calexico" /></li> 879 925 <li>Votes: <input type="text" name="choices-0-votes" value="100" /></li>""") 880 926