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

File 14496.custom-admin-form-exclude.alternative.diff, 6.0 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..73e1e30 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 self.exclude is None and hasattr(self.form, '_meta') and self.form._meta.exclude:
     432            # Take the custom ModelForm's Meta.exclude into account only if the
     433            # ModelAdmin doesn't define its own.
     434            exclude.extend(self.form.Meta.exclude)
    431435        # if exclude is an empty list we pass None to be consistant with the
    432436        # default on modelform_factory
    433437        exclude = exclude or None
    class InlineModelAdmin(BaseModelAdmin): 
    13301334            exclude = list(self.exclude)
    13311335        exclude.extend(kwargs.get("exclude", []))
    13321336        exclude.extend(self.get_readonly_fields(request, obj))
     1337        if self.exclude is None and hasattr(self.form, '_meta') and self.form._meta.exclude:
     1338            # Take the custom ModelForm's Meta.exclude into account only if the
     1339            # InlineModelAdmin doesn't define its own.
     1340            exclude.extend(self.form.Meta.exclude)
    13331341        # if exclude is an empty list we use None, since that's the actual
    13341342        # default
    13351343        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..ba890ec 100644
    a b subclass:: 
    293293
    294294    For an example see the section `Adding custom validation to the admin`_.
    295295
     296    .. admonition:: Note
     297   
     298        If your ``ModelForm`` and ``ModelAdmin`` both define an ``exclude``
     299        option then ``ModelAdmin`` takes precedence::
     300       
     301            class PersonForm(forms.ModelForm):
     302               
     303                class Meta:
     304                    model = Person
     305                    exclude = ['name']
     306       
     307            class PersonAdmin(admin.ModelAdmin):
     308                exclude = ['age']
     309                form = PersonForm
     310               
     311        In the above example, the "age" field will be excluded but the "name"
     312        field will be included in the generated form.
     313
    296314.. attribute:: ModelAdmin.formfield_overrides
    297315
    298316    This provides a quick-and-dirty way to override some of the
  • tests/regressiontests/modeladmin/tests.py

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