Ticket #17642: 17642.patch

File 17642.patch, 11.4 KB (added by michal@…, 3 years ago)

Added support for min_num in admin inlines

  • django/contrib/admin/options.py

    diff --git a/django/contrib/admin/options.py b/django/contrib/admin/options.py
    index ee4ff97..f40074a 100644
    a b class InlineModelAdmin(BaseModelAdmin): 
    13561356    formset = BaseInlineFormSet
    13571357    extra = 3
    13581358    max_num = None
     1359    min_num = None
    13591360    template = None
    13601361    verbose_name = None
    13611362    verbose_name_plural = None
    class InlineModelAdmin(BaseModelAdmin): 
    14091410            "formfield_callback": partial(self.formfield_for_dbfield, request=request),
    14101411            "extra": self.extra,
    14111412            "max_num": self.max_num,
     1413            "min_num": self.min_num,
    14121414            "can_delete": can_delete,
    14131415        }
    14141416        defaults.update(kwargs)
  • django/contrib/contenttypes/generic.py

    diff --git a/django/contrib/contenttypes/generic.py b/django/contrib/contenttypes/generic.py
    index c513787..619f50d 100644
    a b def generic_inlineformset_factory(model, form=ModelForm, 
    424424                                  ct_field="content_type", fk_field="object_id",
    425425                                  fields=None, exclude=None,
    426426                                  extra=3, can_order=False, can_delete=True,
    427                                   max_num=None,
     427                                  max_num=None, min_num=None,
    428428                                  formfield_callback=None):
    429429    """
    430430    Returns an ``GenericInlineFormSet`` for the given kwargs.
    def generic_inlineformset_factory(model, form=ModelForm, 
    449449                                   formfield_callback=formfield_callback,
    450450                                   formset=formset,
    451451                                   extra=extra, can_delete=can_delete, can_order=can_order,
    452                                    fields=fields, exclude=exclude, max_num=max_num)
     452                                   fields=fields, exclude=exclude, max_num=max_num, min_num=min_num)
    453453    FormSet.ct_field = ct_field
    454454    FormSet.ct_fk_field = fk_field
    455455    return FormSet
    class GenericInlineModelAdmin(InlineModelAdmin): 
    486486            "can_order": False,
    487487            "fields": fields,
    488488            "max_num": self.max_num,
     489            "min_num": self.min_num,
    489490            "exclude": exclude
    490491        }
    491492        defaults.update(kwargs)
  • django/forms/models.py

    diff --git a/django/forms/models.py b/django/forms/models.py
    index cd8f027..12cef23 100644
    a b class BaseModelFormSet(BaseFormSet): 
    664664def modelformset_factory(model, form=ModelForm, formfield_callback=None,
    665665                         formset=BaseModelFormSet,
    666666                         extra=1, can_delete=False, can_order=False,
    667                          max_num=None, fields=None, exclude=None):
     667                         max_num=None, min_num=None, fields=None, exclude=None):
    668668    """
    669669    Returns a FormSet class for the given Django model class.
    670670    """
    671671    form = modelform_factory(model, form=form, fields=fields, exclude=exclude,
    672672                             formfield_callback=formfield_callback)
    673     FormSet = formset_factory(form, formset, extra=extra, max_num=max_num,
     673    FormSet = formset_factory(form, formset, extra=extra, max_num=max_num, min_num=min_num,
    674674                              can_order=can_order, can_delete=can_delete)
    675675    FormSet.model = model
    676676    return FormSet
    def _get_foreign_key(parent_model, model, fk_name=None, can_fail=False): 
    806806def inlineformset_factory(parent_model, model, form=ModelForm,
    807807                          formset=BaseInlineFormSet, fk_name=None,
    808808                          fields=None, exclude=None,
    809                           extra=3, can_order=False, can_delete=True, max_num=None,
     809                          extra=3, can_order=False, can_delete=True, max_num=None, min_num=None,
    810810                          formfield_callback=None):
    811811    """
    812812    Returns an ``InlineFormSet`` for the given kwargs.
    def inlineformset_factory(parent_model, model, form=ModelForm, 
    828828        'fields': fields,
    829829        'exclude': exclude,
    830830        'max_num': max_num,
     831        'min_num': min_num,
    831832    }
    832833    FormSet = modelformset_factory(model, **kwargs)
    833834    FormSet.fk = fk
  • docs/ref/contrib/admin/index.txt

    diff --git a/docs/ref/contrib/admin/index.txt b/docs/ref/contrib/admin/index.txt
    index 76ea780..ebd9ae3 100644
    a b The ``InlineModelAdmin`` class adds: 
    14651465    doesn't directly correlate to the number of objects, but can if the value
    14661466    is small enough. See :ref:`model-formsets-max-num` for more information.
    14671467
     1468.. attribute:: InlineModelAdmin.min_num
     1469
     1470    .. versionadded:: 1.4
     1471
     1472    This controls the minumum number of forms to show in the inline.
     1473    See :ref:`formsets-min-num` for more information.
     1474
     1475    .. _ref-contrib-admin-inline-min-num:
     1476
    14681477.. attribute:: InlineModelAdmin.raw_id_fields
    14691478
    14701479    By default, Django's admin uses a select-box interface (<select>) for
  • docs/topics/forms/formsets.txt

    diff --git a/docs/topics/forms/formsets.txt b/docs/topics/forms/formsets.txt
    index 9a91182..0646c60 100644
    a b from ``0`` to ``None`` in version 1.2 to allow ``0`` as a valid value. 
    117117Ensuring the minimum number of forms
    118118------------------------------------
    119119
     120.. versionadded:: 1.4
     121
    120122The ``min_num`` parameter to ``formset_factory`` gives you the ability to make
    121123sure that at least a certain number of forms will be displayed::
    122124
    sure that at least a certain number of forms will be displayed:: 
    133135    <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>
    134136    <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>
    135137
    136 .. versionchanged:: 1.4
    137138
    138139If the value of ``min_num`` is greater than the number of elements that would
    139140be displayed normally (including extra forms), additional blank forms will be
  • tests/regressiontests/generic_inline_admin/admin.py

    diff --git a/tests/regressiontests/generic_inline_admin/admin.py b/tests/regressiontests/generic_inline_admin/admin.py
    index 73cac7f..9b12c70 100644
    a b from django.contrib import admin 
    44from django.contrib.contenttypes import generic
    55
    66from .models import (Media, PhoneNumber, Episode, EpisodeExtra, Contact,
    7     Category, EpisodePermanent, EpisodeMaxNum)
     7    Category, EpisodePermanent, EpisodeMaxNum, EpisodeMinNum)
    88
    99
    1010site = admin.AdminSite(name="admin")
    class MediaMaxNumInline(generic.GenericTabularInline): 
    2929    extra = 5
    3030    max_num = 2
    3131
     32class MediaMinNumInline(generic.GenericTabularInline):
     33    model = Media
     34    extra = 1
     35    min_num = 3
    3236
    3337class PhoneNumberInline(generic.GenericTabularInline):
    3438    model = PhoneNumber
    class MediaPermanentInline(generic.GenericTabularInline): 
    4246site.register(Episode, EpisodeAdmin)
    4347site.register(EpisodeExtra, inlines=[MediaExtraInline])
    4448site.register(EpisodeMaxNum, inlines=[MediaMaxNumInline])
     49site.register(EpisodeMinNum, inlines=[MediaMinNumInline])
    4550site.register(Contact, inlines=[PhoneNumberInline])
    4651site.register(Category)
    4752site.register(EpisodePermanent, inlines=[MediaPermanentInline])
  • tests/regressiontests/generic_inline_admin/models.py

    diff --git a/tests/regressiontests/generic_inline_admin/models.py b/tests/regressiontests/generic_inline_admin/models.py
    index b9426b4..65d9df9 100644
    a b class EpisodeExtra(Episode): 
    4242class EpisodeMaxNum(Episode):
    4343    pass
    4444
     45#
     46# Generic inline with extra and min_num
     47#
     48class EpisodeMinNum(Episode):
     49    pass
    4550
    4651#
    4752# Generic inline with unique_together
  • tests/regressiontests/generic_inline_admin/tests.py

    diff --git a/tests/regressiontests/generic_inline_admin/tests.py b/tests/regressiontests/generic_inline_admin/tests.py
    index db81eec..829a793 100644
    a b from django.test import TestCase 
    1212
    1313# local test models
    1414from .admin import MediaInline, MediaPermanentInline
    15 from .models import (Episode, EpisodeExtra, EpisodeMaxNum, Media,
     15from .models import (Episode, EpisodeExtra, EpisodeMaxNum, EpisodeMinNum, Media,
    1616    EpisodePermanent, Category)
    1717
    1818
    class GenericInlineAdminParametersTest(TestCase): 
    174174        With extra=5 and max_num=2, there should be only 2 forms.
    175175        """
    176176        e = self._create_object(EpisodeMaxNum)
    177         inline_form_data = '<input type="hidden" name="generic_inline_admin-media-content_type-object_id-TOTAL_FORMS" value="2" id="id_generic_inline_admin-media-content_type-object_id-TOTAL_FORMS" /><input type="hidden" name="generic_inline_admin-media-content_type-object_id-INITIAL_FORMS" value="1" id="id_generic_inline_admin-media-content_type-object_id-INITIAL_FORMS" />'
     177        inline_form_data = """<input id="id_generic_inline_admin-media-content_type-object_id-TOTAL_FORMS" name="generic_inline_admin-media-content_type-object_id-TOTAL_FORMS" type="hidden" value="2" /><input id="id_generic_inline_admin-media-content_type-object_id-INITIAL_FORMS" name="generic_inline_admin-media-content_type-object_id-INITIAL_FORMS" type="hidden" value="1" /><input id="id_generic_inline_admin-media-content_type-object_id-MAX_NUM_FORMS" name="generic_inline_admin-media-content_type-object_id-MAX_NUM_FORMS" type="hidden" value="2" /><input id="id_generic_inline_admin-media-content_type-object_id-MIN_NUM_FORMS" name="generic_inline_admin-media-content_type-object_id-MIN_NUM_FORMS" type="hidden" />"""
     178
    178179        response = self.client.get('/generic_inline_admin/admin/generic_inline_admin/episodemaxnum/%s/' % e.pk)
    179180        formset = response.context['inline_admin_formsets'][0].formset
     181
     182        self.assertHTMLEqual( str(formset.management_form), inline_form_data )
    180183        self.assertEqual(formset.total_form_count(), 2)
    181184        self.assertEqual(formset.initial_form_count(), 1)
    182185
     186    def testMinNumParam(self):
     187        """
     188        With extra=1 and min_num=3, there should be exactly 3 forms, one filled and 2 blank.
     189        """
     190        e = self._create_object(EpisodeMinNum)
     191        inline_form_data =  """<input type="hidden" name="generic_inline_admin-media-content_type-object_id-TOTAL_FORMS" value="3" id="id_generic_inline_admin-media-content_type-object_id-TOTAL_FORMS" /><input type="hidden" name="generic_inline_admin-media-content_type-object_id-INITIAL_FORMS" value="1" id="id_generic_inline_admin-media-content_type-object_id-INITIAL_FORMS" /><input type="hidden" name="generic_inline_admin-media-content_type-object_id-MAX_NUM_FORMS" id="id_generic_inline_admin-media-content_type-object_id-MAX_NUM_FORMS" /><input type="hidden" name="generic_inline_admin-media-content_type-object_id-MIN_NUM_FORMS" value="3" id="id_generic_inline_admin-media-content_type-object_id-MIN_NUM_FORMS" />"""
     192
     193        response = self.client.get('/generic_inline_admin/admin/generic_inline_admin/episodeminnum/%s/' % e.pk)
     194        formset = response.context['inline_admin_formsets'][0].formset
     195
     196        self.assertHTMLEqual( str(formset.management_form), inline_form_data )
     197        self.assertEqual(formset.total_form_count(), 3)
     198        self.assertEqual(formset.initial_form_count(), 1)
    183199
    184200class GenericInlineAdminWithUniqueTogetherTest(TestCase):
    185201    urls = "regressiontests.generic_inline_admin.urls"
Back to Top