Django

Code

Changeset 7613

Show
Ignore:
Timestamp:
06/10/08 22:58:01 (3 months ago)
Author:
brosner
Message:

newforms-admin: Fixed #6075 -- Implemented max_num on formsets and model formsets. Added a hook on InlineModelAdmin? to customize in the admin interface.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • django/branches/newforms-admin/django/contrib/admin/options.py

    r7608 r7613  
    700700    formset = BaseInlineFormset 
    701701    extra = 3 
     702    max_num = 0 
    702703    template = None 
    703704    verbose_name = None 
     
    723724            form=self.form, formset=self.formset, fk_name=self.fk_name, 
    724725            fields=fields, formfield_callback=self.formfield_for_dbfield, 
    725             extra=self.extra
     726            extra=self.extra, max_num=self.max_num
    726727 
    727728    def get_fieldsets(self, request, obj=None): 
  • django/branches/newforms-admin/django/newforms/formsets.py

    r7612 r7613  
    1111TOTAL_FORM_COUNT = 'TOTAL_FORMS' 
    1212INITIAL_FORM_COUNT = 'INITIAL_FORMS' 
     13MAX_FORM_COUNT = 'MAX_FORMS' 
    1314ORDERING_FIELD_NAME = 'ORDER' 
    1415DELETION_FIELD_NAME = 'DELETE' 
     
    2324        self.base_fields[TOTAL_FORM_COUNT] = IntegerField(widget=HiddenInput) 
    2425        self.base_fields[INITIAL_FORM_COUNT] = IntegerField(widget=HiddenInput) 
     26        self.base_fields[MAX_FORM_COUNT] = IntegerField(widget=HiddenInput) 
    2527        super(ManagementForm, self).__init__(*args, **kwargs) 
    2628 
     
    3032    """ 
    3133    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): 
    3335        self.is_bound = data is not None or files is not None 
    3436        self.prefix = prefix or 'form' 
     
    4648                self._total_form_count = self.management_form.cleaned_data[TOTAL_FORM_COUNT] 
    4749                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] 
    4851            else: 
    4952                raise ValidationError('ManagementForm data is missing or has been tampered with') 
     
    5154            if initial: 
    5255                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 
    5358                self._total_form_count = self._initial_form_count + self.extra 
    5459            else: 
    5560                self._initial_form_count = 0 
    5661                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} 
    5867            self.management_form = ManagementForm(initial=initial, auto_id=self.auto_id, prefix=self.prefix) 
    59  
     68         
    6069        # construct the forms in the formset 
    6170        self._construct_forms() 
     
    267276 
    268277def formset_factory(form, formset=BaseFormSet, extra=1, can_order=False, 
    269                     can_delete=False): 
     278                    can_delete=False, max_num=0): 
    270279    """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} 
    272283    return type(form.__name__ + 'FormSet', (formset,), attrs) 
    273284 
  • django/branches/newforms-admin/django/newforms/models.py

    r7612 r7613  
    306306        self.queryset = queryset 
    307307        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] 
    309313        defaults.update(kwargs) 
    310314        super(BaseModelFormSet, self).__init__(**defaults) 
     
    370374 
    371375def 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): 
    375379    """ 
    376380    Returns a FormSet class for the given Django model class. 
    377381    """ 
    378382    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) 
    381386    FormSet.model = model 
    382387    return FormSet 
     
    396401     
    397402    def _construct_forms(self): 
    398         from django.newforms.formsets import INITIAL_FORM_COUNT 
    399403        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 
    401405            self._initial_form_count = 0 
    402406        super(BaseInlineFormset, self)._construct_forms() 
     
    444448 
    445449def 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()): 
    450454    """ 
    451455    Returns an ``InlineFormset`` for the given kwargs. 
     
    465469                                    formset=formset, 
    466470                                    extra=extra, can_delete=can_delete, can_order=can_order, 
    467                                     fields=fields, exclude=exclude
     471                                    fields=fields, exclude=exclude, max_num=max_num
    468472    FormSet.fk = fk 
    469473    return FormSet 
  • django/branches/newforms-admin/docs/modelforms.txt

    r7606 r7613  
    450450to the database. 
    451451 
     452Limiting the number of objects editable 
     453--------------------------------------- 
     454 
     455Similar to regular formsets you can use the ``max_num`` parameter to 
     456``modelformset_factory`` to limit the number of forms displayed. With 
     457model formsets this will properly limit the query to only select the maximum 
     458number 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 
     468If the value of ``max_num`` is less than the total objects returned it will 
     469fill 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 
    452480Using ``inlineformset_factory`` 
    453481------------------------------- 
  • django/branches/newforms-admin/docs/newforms.txt

    r7606 r7613  
    22302230that was passed in and two extra forms. Also note that we are passing in a 
    22312231list of dictionaries as the initial data. 
     2232 
     2233Limiting the maximum number of forms 
     2234------------------------------------ 
     2235 
     2236The ``max_num`` parameter to ``formset_factory`` gives you the ability to 
     2237force 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 
     2246The default value of ``max_num`` is ``0`` which is the same as saying put no 
     2247limit on the number forms displayed. 
    22322248 
    22332249Formset validation 
  • django/branches/newforms-admin/tests/modeltests/model_formsets/models.py

    r7605 r7613  
    3232...     'form-TOTAL_FORMS': '3', # the number of forms rendered 
    3333...     'form-INITIAL_FORMS': '0', # the number of forms with initial data 
     34...     'form-MAX_FORMS': '0', # the max number of forms 
    3435...     'form-0-name': 'Charles Baudelaire', 
    3536...     'form-1-name': 'Arthur Rimbaud', 
     
    6970...     'form-TOTAL_FORMS': '3', # the number of forms rendered 
    7071...     'form-INITIAL_FORMS': '2', # the number of forms with initial data 
     72...     'form-MAX_FORMS': '0', # the max number of forms 
    7173...     'form-0-id': '2', 
    7274...     'form-0-name': 'Arthur Rimbaud', 
     
    112114...     'form-TOTAL_FORMS': '4', # the number of forms rendered 
    113115...     'form-INITIAL_FORMS': '3', # the number of forms with initial data 
     116...     'form-MAX_FORMS': '0', # the max number of forms 
    114117...     'form-0-id': '2', 
    115118...     'form-0-name': 'Arthur Rimbaud', 
     
    141144...     'form-TOTAL_FORMS': '4', # the number of forms rendered 
    142145...     'form-INITIAL_FORMS': '3', # the number of forms with initial data 
     146...     'form-MAX_FORMS': '0', # the max number of forms 
    143147...     'form-0-id': '2', 
    144148...     'form-0-name': 'Walt Whitman', 
     
    159163[<Author: Walt Whitman>] 
    160164 
     165Test the behavior of max_num with model formsets. It should properly limit 
     166the queryset to reduce the amount of objects being pulled in when not being 
     167used. 
     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 
    161181# Inline Formsets ############################################################ 
    162182 
     
    179199...     'book_set-TOTAL_FORMS': '3', # the number of forms rendered 
    180200...     'book_set-INITIAL_FORMS': '0', # the number of forms with initial data 
     201...     'book_set-MAX_FORMS': '0', # the max number of forms 
    181202...     'book_set-0-title': 'Les Fleurs du Mal', 
    182203...     'book_set-1-title': '', 
     
    213234...     'book_set-TOTAL_FORMS': '3', # the number of forms rendered 
    214235...     'book_set-INITIAL_FORMS': '1', # the number of forms with initial data 
     236...     'book_set-MAX_FORMS': '0', # the max number of forms 
    215237...     'book_set-0-id': '1', 
    216238...     'book_set-0-title': 'Les Fleurs du Mal', 
     
    239261...     'book_set-TOTAL_FORMS': '3', # the number of forms rendered 
    240262...     'book_set-INITIAL_FORMS': '2', # the number of forms with initial data 
     263...     'book_set-MAX_FORMS': '0', # the max number of forms 
    241264...     'book_set-0-id': '1', 
    242265...     'book_set-0-title': 'Les Fleurs du Mal', 
  • django/branches/newforms-admin/tests/regressiontests/forms/formsets.py

    r7612 r7613  
    2121>>> formset = ChoiceFormSet(auto_id=False, prefix='choices') 
    2222>>> 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" /> 
    2424<tr><th>Choice:</th><td><input type="text" name="choices-0-choice" /></td></tr> 
    2525<tr><th>Votes:</th><td><input type="text" name="choices-0-votes" /></td></tr> 
     
    3535...     'choices-TOTAL_FORMS': '1', # the number of forms rendered 
    3636...     'choices-INITIAL_FORMS': '0', # the number of forms with initial data 
     37...     'choices-MAX_FORMS': '0', # the max number of forms 
    3738...     'choices-0-choice': 'Calexico', 
    3839...     'choices-0-votes': '100', 
     
    6162...     'choices-TOTAL_FORMS': '1', # the number of forms rendered 
    6263...     'choices-INITIAL_FORMS': '0', # the number of forms with initial data 
     64...     'choices-MAX_FORMS': '0', # the max number of forms 
    6365...     'choices-0-choice': 'Calexico', 
    6466...     'choices-0-votes': '', 
     
    9193...     'choices-TOTAL_FORMS': '2', # the number of forms rendered 
    9294...     'choices-INITIAL_FORMS': '1', # the number of forms with initial data 
     95...     'choices-MAX_FORMS': '0', # the max number of forms 
    9396...     'choices-0-choice': 'Calexico', 
    9497...     'choices-0-votes': '100', 
     
    112115...     'choices-TOTAL_FORMS': '2', # the number of forms rendered 
    113116...     'choices-INITIAL_FORMS': '1', # the number of forms with initial data 
     117...     'choices-MAX_FORMS': '0', # the max number of forms 
    114118...     'choices-0-choice': 'Calexico', 
    115119...     'choices-0-votes': '100', 
     
    131135...     'choices-TOTAL_FORMS': '2', # the number of forms rendered 
    132136...     'choices-INITIAL_FORMS': '1', # the number of forms with initial data 
     137...     'choices-MAX_FORMS': '0', # the max number of forms 
    133138...     'choices-0-choice': '', # deleted value 
    134139...     'choices-0-votes': '', # deleted value 
     
    168173...     'choices-TOTAL_FORMS': '3', # the number of forms rendered 
    169174...     'choices-INITIAL_FORMS': '0', # the number of forms with initial data 
     175...     'choices-MAX_FORMS': '0', # the max number of forms 
    170176...     'choices-0-choice': '', 
    171177...     'choices-0-votes': '', 
     
    188194...     'choices-TOTAL_FORMS': '3', # the number of forms rendered 
    189195...     'choices-INITIAL_FORMS': '0', # the number of forms with initial data 
     196...     'choices-MAX_FORMS': '0', # the max number of forms 
    190197...     'choices-0-choice': 'Calexico', 
    191198...     'choices-0-votes': '100', 
     
    208215...     'choices-TOTAL_FORMS': '3', # the number of forms rendered 
    209216...     'choices-INITIAL_FORMS': '0', # the number of forms with initial data 
     217...     'choices-MAX_FORMS': '0', # the max number of forms 
    210218...     'choices-0-choice': 'Calexico', 
    211219...     'choices-0-votes': '100', 
     
    268276...     'choices-TOTAL_FORMS': '3', # the number of forms rendered 
    269277...     'choices-INITIAL_FORMS': '2', # the number of forms with initial data 
     278...     'choices-MAX_FORMS': '0', # the max number of forms 
    270279...     'choices-0-choice': 'Calexico', 
    271280...     'choices-0-votes': '100', 
     
    317326...     'choices-TOTAL_FORMS': '3', # the number of forms rendered 
    318327...     'choices-INITIAL_FORMS': '2', # the number of forms with initial data 
     328...     'choices-MAX_FORMS': '0', # the max number of forms 
    319329...     'choices-0-choice': 'Calexico', 
    320330...     'choices-0-votes': '100', 
     
    343353...     'choices-TOTAL_FORMS': '4', # the number of forms rendered 
    344354...     'choices-INITIAL_FORMS': '3', # the number of forms with initial data 
     355...     'choices-MAX_FORMS': '0', # the max number of forms 
    345356...     'choices-0-choice': 'Calexico', 
    346357...     'choices-0-votes': '100', 
     
    404415...     'choices-TOTAL_FORMS': '4', # the number of forms rendered 
    405416...     'choices-INITIAL_FORMS': '3', # the number of forms with initial data 
     417...     'choices-MAX_FORMS': '0', # the max number of forms 
    406418...     'choices-0-choice': 'Calexico', 
    407419...     'choices-0-votes': '100', 
     
    445457... 
    446458 
    447 >>> class FavoriteDrinksFormSet(BaseFormSet): 
    448 ...     form = FavoriteDrinkForm 
    449 ...     extra = 2 
    450 ...     can_order = False 
    451 ...     can_delete = False 
    452 ... 
     459>>> class BaseFavoriteDrinksFormSet(BaseFormSet): 
    453460...     def clean(self): 
    454461...         seen_drinks = [] 
     
    459466... 
    460467 
     468>>> FavoriteDrinksFormSet = formset_factory(FavoriteDrinkForm, 
     469...     formset=BaseFavoriteDrinksFormSet, extra=3) 
     470 
    461471We start out with a some duplicate data. 
    462472 
     
    464474...     'drinks-TOTAL_FORMS': '2', # the number of forms rendered 
    465475...     'drinks-INITIAL_FORMS': '0', # the number of forms with initial data 
     476...     'drinks-MAX_FORMS': '0', # the max number of forms 
    466477...     'drinks-0-name': 'Gin and Tonic', 
    467478...     'drinks-1-name': 'Gin and Tonic', 
     
    485496...     'drinks-TOTAL_FORMS': '2', # the number of forms rendered 
    486497...     'drinks-INITIAL_FORMS': '0', # the number of forms with initial data 
     498...     'drinks-MAX_FORMS': '0', # the max number of forms 
    487499...     'drinks-0-name': 'Gin and Tonic', 
    488500...     'drinks-1-name': 'Bloody Mary', 
     
    494506>>> for error in formset.non_form_errors(): 
    495507...     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> 
    496557 
    497558