Changeset 7613
- Timestamp:
- 06/10/08 22:58:01 (3 months ago)
- Files:
-
- django/branches/newforms-admin/django/contrib/admin/options.py (modified) (2 diffs)
- django/branches/newforms-admin/django/newforms/formsets.py (modified) (6 diffs)
- django/branches/newforms-admin/django/newforms/models.py (modified) (5 diffs)
- django/branches/newforms-admin/docs/modelforms.txt (modified) (1 diff)
- django/branches/newforms-admin/docs/newforms.txt (modified) (1 diff)
- django/branches/newforms-admin/tests/modeltests/model_formsets/models.py (modified) (8 diffs)
- django/branches/newforms-admin/tests/regressiontests/forms/formsets.py (modified) (18 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
django/branches/newforms-admin/django/contrib/admin/options.py
r7608 r7613 700 700 formset = BaseInlineFormset 701 701 extra = 3 702 max_num = 0 702 703 template = None 703 704 verbose_name = None … … 723 724 form=self.form, formset=self.formset, fk_name=self.fk_name, 724 725 fields=fields, formfield_callback=self.formfield_for_dbfield, 725 extra=self.extra )726 extra=self.extra, max_num=self.max_num) 726 727 727 728 def get_fieldsets(self, request, obj=None): django/branches/newforms-admin/django/newforms/formsets.py
r7612 r7613 11 11 TOTAL_FORM_COUNT = 'TOTAL_FORMS' 12 12 INITIAL_FORM_COUNT = 'INITIAL_FORMS' 13 MAX_FORM_COUNT = 'MAX_FORMS' 13 14 ORDERING_FIELD_NAME = 'ORDER' 14 15 DELETION_FIELD_NAME = 'DELETE' … … 23 24 self.base_fields[TOTAL_FORM_COUNT] = IntegerField(widget=HiddenInput) 24 25 self.base_fields[INITIAL_FORM_COUNT] = IntegerField(widget=HiddenInput) 26 self.base_fields[MAX_FORM_COUNT] = IntegerField(widget=HiddenInput) 25 27 super(ManagementForm, self).__init__(*args, **kwargs) 26 28 … … 30 32 """ 31 33 def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None, 32 initial=None, error_class=ErrorList):34 initial=None, error_class=ErrorList): 33 35 self.is_bound = data is not None or files is not None 34 36 self.prefix = prefix or 'form' … … 46 48 self._total_form_count = self.management_form.cleaned_data[TOTAL_FORM_COUNT] 47 49 self._initial_form_count = self.management_form.cleaned_data[INITIAL_FORM_COUNT] 50 self._max_form_count = self.management_form.cleaned_data[MAX_FORM_COUNT] 48 51 else: 49 52 raise ValidationError('ManagementForm data is missing or has been tampered with') … … 51 54 if initial: 52 55 self._initial_form_count = len(initial) 56 if self._initial_form_count > self._max_form_count and self._max_form_count > 0: 57 self._initial_form_count = self._max_form_count 53 58 self._total_form_count = self._initial_form_count + self.extra 54 59 else: 55 60 self._initial_form_count = 0 56 61 self._total_form_count = self.extra 57 initial = {TOTAL_FORM_COUNT: self._total_form_count, INITIAL_FORM_COUNT: self._initial_form_count} 62 if self._total_form_count > self._max_form_count and self._max_form_count > 0: 63 self._total_form_count = self._max_form_count 64 initial = {TOTAL_FORM_COUNT: self._total_form_count, 65 INITIAL_FORM_COUNT: self._initial_form_count, 66 MAX_FORM_COUNT: self._max_form_count} 58 67 self.management_form = ManagementForm(initial=initial, auto_id=self.auto_id, prefix=self.prefix) 59 68 60 69 # construct the forms in the formset 61 70 self._construct_forms() … … 267 276 268 277 def formset_factory(form, formset=BaseFormSet, extra=1, can_order=False, 269 can_delete=False ):278 can_delete=False, max_num=0): 270 279 """Return a FormSet for the given form class.""" 271 attrs = {'form': form, 'extra': extra, 'can_order': can_order, 'can_delete': can_delete} 280 attrs = {'form': form, 'extra': extra, 281 'can_order': can_order, 'can_delete': can_delete, 282 '_max_form_count': max_num} 272 283 return type(form.__name__ + 'FormSet', (formset,), attrs) 273 284 django/branches/newforms-admin/django/newforms/models.py
r7612 r7613 306 306 self.queryset = queryset 307 307 defaults = {'data': data, 'files': files, 'auto_id': auto_id, 'prefix': prefix} 308 defaults['initial'] = [model_to_dict(obj) for obj in self.get_queryset()] 308 if self._max_form_count > 0: 309 qs = self.get_queryset()[:self._max_form_count] 310 else: 311 qs = self.get_queryset() 312 defaults['initial'] = [model_to_dict(obj) for obj in qs] 309 313 defaults.update(kwargs) 310 314 super(BaseModelFormSet, self).__init__(**defaults) … … 370 374 371 375 def modelformset_factory(model, form=ModelForm, formfield_callback=lambda f: f.formfield(), 372 formset=BaseModelFormSet,373 extra=1, can_delete=False, can_order=False,374 fields=None, exclude=None):376 formset=BaseModelFormSet, 377 extra=1, can_delete=False, can_order=False, 378 max_num=0, fields=None, exclude=None): 375 379 """ 376 380 Returns a FormSet class for the given Django model class. 377 381 """ 378 382 form = modelform_factory(model, form=form, fields=fields, exclude=exclude, 379 formfield_callback=formfield_callback) 380 FormSet = formset_factory(form, formset, extra=extra, can_order=can_order, can_delete=can_delete) 383 formfield_callback=formfield_callback) 384 FormSet = formset_factory(form, formset, extra=extra, max_num=max_num, 385 can_order=can_order, can_delete=can_delete) 381 386 FormSet.model = model 382 387 return FormSet … … 396 401 397 402 def _construct_forms(self): 398 from django.newforms.formsets import INITIAL_FORM_COUNT399 403 if self.save_as_new: 400 self._total_form_count = self. management_form.cleaned_data[INITIAL_FORM_COUNT]404 self._total_form_count = self._initial_form_count 401 405 self._initial_form_count = 0 402 406 super(BaseInlineFormset, self)._construct_forms() … … 444 448 445 449 def inlineformset_factory(parent_model, model, form=ModelForm, 446 formset=BaseInlineFormset, fk_name=None,447 fields=None, exclude=None,448 extra=3, can_order=False, can_delete=True,449 formfield_callback=lambda f: f.formfield()):450 formset=BaseInlineFormset, fk_name=None, 451 fields=None, exclude=None, 452 extra=3, can_order=False, can_delete=True, max_num=0, 453 formfield_callback=lambda f: f.formfield()): 450 454 """ 451 455 Returns an ``InlineFormset`` for the given kwargs. … … 465 469 formset=formset, 466 470 extra=extra, can_delete=can_delete, can_order=can_order, 467 fields=fields, exclude=exclude )471 fields=fields, exclude=exclude, max_num=max_num) 468 472 FormSet.fk = fk 469 473 return FormSet django/branches/newforms-admin/docs/modelforms.txt
r7606 r7613 450 450 to the database. 451 451 452 Limiting the number of objects editable 453 --------------------------------------- 454 455 Similar to regular formsets you can use the ``max_num`` parameter to 456 ``modelformset_factory`` to limit the number of forms displayed. With 457 model formsets this will properly limit the query to only select the maximum 458 number of objects needed:: 459 460 >>> Author.objects.order_by('name') 461 [<Author: Charles Baudelaire>, <Author: Paul Verlaine>, <Author: Walt Whitman>] 462 463 >>> AuthorFormSet = modelformset_factory(Author, max_num=2, extra=1) 464 >>> formset = AuthorFormSet(queryset=Author.objects.order_by('name')) 465 >>> formset.initial 466 [{'id': 1, 'name': u'Charles Baudelaire'}, {'id': 3, 'name': u'Paul Verlaine'}] 467 468 If the value of ``max_num`` is less than the total objects returned it will 469 fill the rest with extra forms:: 470 471 >>> AuthorFormSet = modelformset_factory(Author, max_num=4, extra=1) 472 >>> formset = AuthorFormSet(queryset=Author.objects.order_by('name')) 473 >>> for form in formset.forms: 474 ... print form.as_table() 475 <tr><th><label for="id_form-0-name">Name:</label></th><td><input id="id_form-0-name" type="text" name="form-0-name" value="Charles Baudelaire" maxlength="100" /><input type="hidden" name="form-0-id" value="1" id="id_form-0-id" /></td></tr> 476 <tr><th><label for="id_form-1-name">Name:</label></th><td><input id="id_form-1-name" type="text" name="form-1-name" value="Paul Verlaine" maxlength="100" /><input type="hidden" name="form-1-id" value="3" id="id_form-1-id" /></td></tr> 477 <tr><th><label for="id_form-2-name">Name:</label></th><td><input id="id_form-2-name" type="text" name="form-2-name" value="Walt Whitman" maxlength="100" /><input type="hidden" name="form-2-id" value="2" id="id_form-2-id" /></td></tr> 478 <tr><th><label for="id_form-3-name">Name:</label></th><td><input id="id_form-3-name" type="text" name="form-3-name" maxlength="100" /><input type="hidden" name="form-3-id" id="id_form-3-id" /></td></tr> 479 452 480 Using ``inlineformset_factory`` 453 481 ------------------------------- django/branches/newforms-admin/docs/newforms.txt
r7606 r7613 2230 2230 that was passed in and two extra forms. Also note that we are passing in a 2231 2231 list of dictionaries as the initial data. 2232 2233 Limiting the maximum number of forms 2234 ------------------------------------ 2235 2236 The ``max_num`` parameter to ``formset_factory`` gives you the ability to 2237 force the maximum number of forms the formset will display:: 2238 2239 >>> ArticleFormSet = formset_factory(ArticleForm, extra=2, max_num=1) 2240 >>> formset = ArticleFormset() 2241 >>> for form in formset.forms: 2242 ... print form.as_table() 2243 <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> 2244 <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> 2245 2246 The default value of ``max_num`` is ``0`` which is the same as saying put no 2247 limit on the number forms displayed. 2232 2248 2233 2249 Formset validation django/branches/newforms-admin/tests/modeltests/model_formsets/models.py
r7605 r7613 32 32 ... 'form-TOTAL_FORMS': '3', # the number of forms rendered 33 33 ... 'form-INITIAL_FORMS': '0', # the number of forms with initial data 34 ... 'form-MAX_FORMS': '0', # the max number of forms 34 35 ... 'form-0-name': 'Charles Baudelaire', 35 36 ... 'form-1-name': 'Arthur Rimbaud', … … 69 70 ... 'form-TOTAL_FORMS': '3', # the number of forms rendered 70 71 ... 'form-INITIAL_FORMS': '2', # the number of forms with initial data 72 ... 'form-MAX_FORMS': '0', # the max number of forms 71 73 ... 'form-0-id': '2', 72 74 ... 'form-0-name': 'Arthur Rimbaud', … … 112 114 ... 'form-TOTAL_FORMS': '4', # the number of forms rendered 113 115 ... 'form-INITIAL_FORMS': '3', # the number of forms with initial data 116 ... 'form-MAX_FORMS': '0', # the max number of forms 114 117 ... 'form-0-id': '2', 115 118 ... 'form-0-name': 'Arthur Rimbaud', … … 141 144 ... 'form-TOTAL_FORMS': '4', # the number of forms rendered 142 145 ... 'form-INITIAL_FORMS': '3', # the number of forms with initial data 146 ... 'form-MAX_FORMS': '0', # the max number of forms 143 147 ... 'form-0-id': '2', 144 148 ... 'form-0-name': 'Walt Whitman', … … 159 163 [<Author: Walt Whitman>] 160 164 165 Test the behavior of max_num with model formsets. It should properly limit 166 the queryset to reduce the amount of objects being pulled in when not being 167 used. 168 169 >>> qs = Author.objects.order_by('name') 170 171 >>> AuthorFormSet = modelformset_factory(Author, max_num=2) 172 >>> formset = AuthorFormSet(queryset=qs) 173 >>> formset.initial 174 [{'id': 1, 'name': u'Charles Baudelaire'}, {'id': 3, 'name': u'Paul Verlaine'}] 175 176 >>> AuthorFormSet = modelformset_factory(Author, max_num=3) 177 >>> formset = AuthorFormSet(queryset=qs) 178 >>> formset.initial 179 [{'id': 1, 'name': u'Charles Baudelaire'}, {'id': 3, 'name': u'Paul Verlaine'}, {'id': 2, 'name': u'Walt Whitman'}] 180 161 181 # Inline Formsets ############################################################ 162 182 … … 179 199 ... 'book_set-TOTAL_FORMS': '3', # the number of forms rendered 180 200 ... 'book_set-INITIAL_FORMS': '0', # the number of forms with initial data 201 ... 'book_set-MAX_FORMS': '0', # the max number of forms 181 202 ... 'book_set-0-title': 'Les Fleurs du Mal', 182 203 ... 'book_set-1-title': '', … … 213 234 ... 'book_set-TOTAL_FORMS': '3', # the number of forms rendered 214 235 ... 'book_set-INITIAL_FORMS': '1', # the number of forms with initial data 236 ... 'book_set-MAX_FORMS': '0', # the max number of forms 215 237 ... 'book_set-0-id': '1', 216 238 ... 'book_set-0-title': 'Les Fleurs du Mal', … … 239 261 ... 'book_set-TOTAL_FORMS': '3', # the number of forms rendered 240 262 ... 'book_set-INITIAL_FORMS': '2', # the number of forms with initial data 263 ... 'book_set-MAX_FORMS': '0', # the max number of forms 241 264 ... 'book_set-0-id': '1', 242 265 ... 'book_set-0-title': 'Les Fleurs du Mal', django/branches/newforms-admin/tests/regressiontests/forms/formsets.py
r7612 r7613 21 21 >>> formset = ChoiceFormSet(auto_id=False, prefix='choices') 22 22 >>> print formset 23 <input type="hidden" name="choices-TOTAL_FORMS" value="1" /><input type="hidden" name="choices-INITIAL_FORMS" value="0" /> 23 <input type="hidden" name="choices-TOTAL_FORMS" value="1" /><input type="hidden" name="choices-INITIAL_FORMS" value="0" /><input type="hidden" name="choices-MAX_FORMS" value="0" /> 24 24 <tr><th>Choice:</th><td><input type="text" name="choices-0-choice" /></td></tr> 25 25 <tr><th>Votes:</th><td><input type="text" name="choices-0-votes" /></td></tr> … … 35 35 ... 'choices-TOTAL_FORMS': '1', # the number of forms rendered 36 36 ... 'choices-INITIAL_FORMS': '0', # the number of forms with initial data 37 ... 'choices-MAX_FORMS': '0', # the max number of forms 37 38 ... 'choices-0-choice': 'Calexico', 38 39 ... 'choices-0-votes': '100', … … 61 62 ... 'choices-TOTAL_FORMS': '1', # the number of forms rendered 62 63 ... 'choices-INITIAL_FORMS': '0', # the number of forms with initial data 64 ... 'choices-MAX_FORMS': '0', # the max number of forms 63 65 ... 'choices-0-choice': 'Calexico', 64 66 ... 'choices-0-votes': '', … … 91 93 ... 'choices-TOTAL_FORMS': '2', # the number of forms rendered 92 94 ... 'choices-INITIAL_FORMS': '1', # the number of forms with initial data 95 ... 'choices-MAX_FORMS': '0', # the max number of forms 93 96 ... 'choices-0-choice': 'Calexico', 94 97 ... 'choices-0-votes': '100', … … 112 115 ... 'choices-TOTAL_FORMS': '2', # the number of forms rendered 113 116 ... 'choices-INITIAL_FORMS': '1', # the number of forms with initial data 117 ... 'choices-MAX_FORMS': '0', # the max number of forms 114 118 ... 'choices-0-choice': 'Calexico', 115 119 ... 'choices-0-votes': '100', … … 131 135 ... 'choices-TOTAL_FORMS': '2', # the number of forms rendered 132 136 ... 'choices-INITIAL_FORMS': '1', # the number of forms with initial data 137 ... 'choices-MAX_FORMS': '0', # the max number of forms 133 138 ... 'choices-0-choice': '', # deleted value 134 139 ... 'choices-0-votes': '', # deleted value … … 168 173 ... 'choices-TOTAL_FORMS': '3', # the number of forms rendered 169 174 ... 'choices-INITIAL_FORMS': '0', # the number of forms with initial data 175 ... 'choices-MAX_FORMS': '0', # the max number of forms 170 176 ... 'choices-0-choice': '', 171 177 ... 'choices-0-votes': '', … … 188 194 ... 'choices-TOTAL_FORMS': '3', # the number of forms rendered 189 195 ... 'choices-INITIAL_FORMS': '0', # the number of forms with initial data 196 ... 'choices-MAX_FORMS': '0', # the max number of forms 190 197 ... 'choices-0-choice': 'Calexico', 191 198 ... 'choices-0-votes': '100', … … 208 215 ... 'choices-TOTAL_FORMS': '3', # the number of forms rendered 209 216 ... 'choices-INITIAL_FORMS': '0', # the number of forms with initial data 217 ... 'choices-MAX_FORMS': '0', # the max number of forms 210 218 ... 'choices-0-choice': 'Calexico', 211 219 ... 'choices-0-votes': '100', … … 268 276 ... 'choices-TOTAL_FORMS': '3', # the number of forms rendered 269 277 ... 'choices-INITIAL_FORMS': '2', # the number of forms with initial data 278 ... 'choices-MAX_FORMS': '0', # the max number of forms 270 279 ... 'choices-0-choice': 'Calexico', 271 280 ... 'choices-0-votes': '100', … … 317 326 ... 'choices-TOTAL_FORMS': '3', # the number of forms rendered 318 327 ... 'choices-INITIAL_FORMS': '2', # the number of forms with initial data 328 ... 'choices-MAX_FORMS': '0', # the max number of forms 319 329 ... 'choices-0-choice': 'Calexico', 320 330 ... 'choices-0-votes': '100', … … 343 353 ... 'choices-TOTAL_FORMS': '4', # the number of forms rendered 344 354 ... 'choices-INITIAL_FORMS': '3', # the number of forms with initial data 355 ... 'choices-MAX_FORMS': '0', # the max number of forms 345 356 ... 'choices-0-choice': 'Calexico', 346 357 ... 'choices-0-votes': '100', … … 404 415 ... 'choices-TOTAL_FORMS': '4', # the number of forms rendered 405 416 ... 'choices-INITIAL_FORMS': '3', # the number of forms with initial data 417 ... 'choices-MAX_FORMS': '0', # the max number of forms 406 418 ... 'choices-0-choice': 'Calexico', 407 419 ... 'choices-0-votes': '100', … … 445 457 ... 446 458 447 >>> class FavoriteDrinksFormSet(BaseFormSet): 448 ... form = FavoriteDrinkForm 449 ... extra = 2 450 ... can_order = False 451 ... can_delete = False 452 ... 459 >>> class BaseFavoriteDrinksFormSet(BaseFormSet): 453 460 ... def clean(self): 454 461 ... seen_drinks = [] … … 459 466 ... 460 467 468 >>> FavoriteDrinksFormSet = formset_factory(FavoriteDrinkForm, 469 ... formset=BaseFavoriteDrinksFormSet, extra=3) 470 461 471 We start out with a some duplicate data. 462 472 … … 464 474 ... 'drinks-TOTAL_FORMS': '2', # the number of forms rendered 465 475 ... 'drinks-INITIAL_FORMS': '0', # the number of forms with initial data 476 ... 'drinks-MAX_FORMS': '0', # the max number of forms 466 477 ... 'drinks-0-name': 'Gin and Tonic', 467 478 ... 'drinks-1-name': 'Gin and Tonic', … … 485 496 ... 'drinks-TOTAL_FORMS': '2', # the number of forms rendered 486 497 ... 'drinks-INITIAL_FORMS': '0', # the number of forms with initial data 498 ... 'drinks-MAX_FORMS': '0', # the max number of forms 487 499 ... 'drinks-0-name': 'Gin and Tonic', 488 500 ... 'drinks-1-name': 'Bloody Mary', … … 494 506 >>> for error in formset.non_form_errors(): 495 507 ... print error 508 509 # Limiting the maximum number of forms ######################################## 510 511 # Base case for max_num. 512 513 >>> LimitedFavoriteDrinkFormSet = formset_factory(FavoriteDrinkForm, extra=5, max_num=2) 514 >>> formset = LimitedFavoriteDrinkFormSet() 515 >>> for form in formset.forms: 516 ... print form 517 <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> 518 <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> 519 520 # Ensure the that max_num has no affect when extra is less than max_forms. 521 522 >>> LimitedFavoriteDrinkFormSet = formset_factory(FavoriteDrinkForm, extra=1, max_num=2) 523 >>> formset = LimitedFavoriteDrinkFormSet() 524 >>> for form in formset.forms: 525 ... print form 526 <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> 527 528 # max_num with initial data 529 530 # More initial forms than max_num will result in only the first max_num of 531 # them to be displayed with no extra forms. 532 533 >>> initial = [ 534 ... {'name': 'Gin Tonic'}, 535 ... {'name': 'Bloody Mary'}, 536 ... {'name': 'Jack and Coke'}, 537 ... ] 538 >>> LimitedFavoriteDrinkFormSet = formset_factory(FavoriteDrinkForm, extra=1, max_num=2) 539 >>> formset = LimitedFavoriteDrinkFormSet(initial=initial) 540 >>> for form in formset.forms: 541 ... print form 542 <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> 543 <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> 544 545 # One form from initial and extra=3 with max_num=2 should result in the one 546 # initial form and one extra. 547 548 >>> initial = [ 549 ... {'name': 'Gin Tonic'}, 550 ... ] 551 >>> LimitedFavoriteDrinkFormSet = formset_factory(FavoriteDrinkForm, extra=3, max_num=2) 552 >>> formset = LimitedFavoriteDrinkFormSet(initial=initial) 553 >>> for form in formset.forms: 554 ... print form 555 <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> 556 <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> 496 557 497 558
