Ticket #14496: 14496.custom-admin-form-exclude.diff

File 14496.custom-admin-form-exclude.diff, 6.8 KB (added by julien, 4 years ago)
  • django/contrib/admin/options.py

    diff --git a/django/contrib/admin/options.py b/django/contrib/admin/options.py
    index c603210..af0c146 100644
    a b class ModelAdmin(BaseModelAdmin): 
    428428            exclude = list(self.exclude)
    429429        exclude.extend(kwargs.get("exclude", []))
    430430        exclude.extend(self.get_readonly_fields(request, obj))
     431        if hasattr(self.form, '_meta') and self.form._meta.exclude:
     432            # Take into account the custom ModelForm's Meta.exclude.
     433            exclude.extend(self.form.Meta.exclude)
    431434        # if exclude is an empty list we pass None to be consistant with the
    432435        # default on modelform_factory
    433436        exclude = exclude or None
    class InlineModelAdmin(BaseModelAdmin): 
    13301333            exclude = list(self.exclude)
    13311334        exclude.extend(kwargs.get("exclude", []))
    13321335        exclude.extend(self.get_readonly_fields(request, obj))
     1336        if hasattr(self.form, '_meta') and self.form._meta.exclude:
     1337            # Take into account the custom ModelForm's Meta.exclude.
     1338            exclude.extend(self.form.Meta.exclude)
    13331339        # if exclude is an empty list we use None, since that's the actual
    13341340        # default
    13351341        exclude = exclude or None
  • docs/ref/contrib/admin/index.txt

    diff --git a/docs/ref/contrib/admin/index.txt b/docs/ref/contrib/admin/index.txt
    index fb48ba8..ba1593d 100644
    a b subclass:: 
    293293
    294294    For an example see the section `Adding custom validation to the admin`_.
    295295
     296    .. admonition:: Note
     297   
     298        .. versionchanged:: 1.4
     299   
     300        If your own ``ModelForm`` defines an ``exclude`` list of fields in its
     301        ``Meta`` options, then that list of fields will automatically
     302        be combined to that of the ``ModelAdmin``'s own ``exclude`` list::
     303       
     304            class PersonForm(forms.ModelForm):
     305               
     306                class Meta:
     307                    model = Person
     308                    exclude = ['name']
     309       
     310            class PersonAdmin(admin.ModelAdmin):
     311                exclude = ['age']
     312                form = PersonForm
     313               
     314        In the above example, the two fields "name" and "age" will be excluded from the
     315        generated form.
     316
    296317.. attribute:: ModelAdmin.formfield_overrides
    297318
    298319    This provides a quick-and-dirty way to override some of the
  • docs/releases/1.4.txt

    diff --git a/docs/releases/1.4.txt b/docs/releases/1.4.txt
    index 6a126ed..fef786a 100644
    a b A new helper function, 
    6060``template.Library`` to ease the creation of template tags that store some
    6161data in a specified context variable.
    6262
     63``exclude`` in ``ModelAdmin`` and custom admin forms
     64~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     65
     66If you define a custom ``ModelForm`` in a ``ModelAdmin`` and that form
     67defines an ``exclude`` list of fields in its ``Meta`` options, then that list
     68of fields will automatically be combined to that of the ``ModelAdmin``'s own
     69``exclude`` list::
     70
     71    class PersonForm(forms.ModelForm):
     72       
     73        class Meta:
     74            model = Person
     75            exclude = ['name']
     76
     77    class PersonAdmin(admin.ModelAdmin):
     78        exclude = ['age']
     79        form = PersonForm
     80       
     81In the above example, the two fields "name" and "age" will be excluded from the
     82generated form.
     83
    6384.. _backwards-incompatible-changes-1.4:
    6485
    6586Backwards incompatible changes in 1.4
  • tests/regressiontests/modeladmin/tests.py

    diff --git a/tests/regressiontests/modeladmin/tests.py b/tests/regressiontests/modeladmin/tests.py
    index 530b476..70b3e3e 100644
    a b class ModelAdminTests(TestCase): 
    119119        ma = BandAdmin(Band, self.site)
    120120        self.assertEqual(ma.get_form(request).base_fields.keys(),
    121121            ['name'])
     122       
     123    def test_custom_form_meta_exclude_with_readonly(self):
     124        """
     125        Ensure that the custom ModelForm's `Meta.exclude` is respected when
     126        used in conjunction with readonly fields.
     127        Refs #14496.
     128        """
     129        # First, with `ModelAdmin` -----------------------
     130       
     131        class AdminBandForm(forms.ModelForm):
     132
     133            class Meta:
     134                model = Band
     135                exclude = ['bio']
    122136
     137        class BandAdmin(ModelAdmin):
     138            readonly_fields = ['name']
     139            form = AdminBandForm
     140       
     141        ma = BandAdmin(Band, self.site)
     142        self.assertEqual(ma.get_form(request).base_fields.keys(),
     143            ['sign_date',])
     144   
     145        # Then, with `InlineModelAdmin`  -----------------
     146       
     147        class AdminConcertForm(forms.ModelForm):
     148
     149            class Meta:
     150                model = Concert
     151                exclude = ['day']
     152       
     153        class ConcertInline(TabularInline):
     154            readonly_fields = ['transport']
     155            form = AdminConcertForm
     156            fk_name = 'main_band'
     157            model = Concert
     158           
     159        class BandAdmin(ModelAdmin):
     160            inlines = [
     161                ConcertInline
     162            ]
     163
     164        ma = BandAdmin(Band, self.site)
     165        self.assertEqual(
     166            list(ma.get_formsets(request))[0]().forms[0].fields.keys(),
     167            ['main_band', 'opening_band', 'id', 'DELETE',])
     168       
     169    def test_custom_form_meta_exclude(self):
     170        """
     171        Ensure that the custom ModelForm's `Meta.exclude` is respected.
     172        Refs #14496.
     173        """
     174        # First, with `ModelAdmin` -----------------------
     175       
     176        class AdminBandForm(forms.ModelForm):
     177
     178            class Meta:
     179                model = Band
     180                exclude = ['bio']
     181
     182        class BandAdmin(ModelAdmin):
     183            exclude = ['name']
     184            form = AdminBandForm
     185       
     186        ma = BandAdmin(Band, self.site)
     187        self.assertEqual(ma.get_form(request).base_fields.keys(),
     188            ['sign_date',])
     189       
     190        # Then, with `InlineModelAdmin`  -----------------
     191       
     192        class AdminConcertForm(forms.ModelForm):
     193
     194            class Meta:
     195                model = Concert
     196                exclude = ['day']
     197       
     198        class ConcertInline(TabularInline):
     199            exclude = ['transport']
     200            form = AdminConcertForm
     201            fk_name = 'main_band'
     202            model = Concert
     203           
     204        class BandAdmin(ModelAdmin):
     205            inlines = [
     206                ConcertInline
     207            ]
     208
     209        ma = BandAdmin(Band, self.site)
     210        self.assertEqual(
     211            list(ma.get_formsets(request))[0]().forms[0].fields.keys(),
     212            ['main_band', 'opening_band', 'id', 'DELETE',])
     213       
    123214    def test_custom_form_validation(self):
    124215        # If we specify a form, it should use it allowing custom validation to work
    125216        # properly. This won't, however, break any of the admin widgets or media.
Back to Top