Commit 3d027b72 authored by Jannis Leidel's avatar Jannis Leidel
Browse files

Fixed #14496 -- Fixed conflict between ModelForm exclude and ModelAdmin...

Fixed #14496 -- Fixed conflict between ModelForm exclude and ModelAdmin readonly values. Thanks, Julien Phalip.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@16602 bcc190cf-cafb-0310-a4f2-bffc1f526a37
parent e912edec
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -437,6 +437,10 @@ class ModelAdmin(BaseModelAdmin):
        else:
            exclude = list(self.exclude)
        exclude.extend(self.get_readonly_fields(request, obj))
        if self.exclude is None and hasattr(self.form, '_meta') and self.form._meta.exclude:
            # Take the custom ModelForm's Meta.exclude into account only if the
            # ModelAdmin doesn't define its own.
            exclude.extend(self.form._meta.exclude)
        # if exclude is an empty list we pass None to be consistant with the
        # default on modelform_factory
        exclude = exclude or None
@@ -1343,6 +1347,10 @@ class InlineModelAdmin(BaseModelAdmin):
        else:
            exclude = list(self.exclude)
        exclude.extend(self.get_readonly_fields(request, obj))
        if self.exclude is None and hasattr(self.form, '_meta') and self.form._meta.exclude:
            # Take the custom ModelForm's Meta.exclude into account only if the
            # InlineModelAdmin doesn't define its own.
            exclude.extend(self.form._meta.exclude)
        # if exclude is an empty list we use None, since that's the actual
        # default
        exclude = exclude or None
+18 −0
Original line number Diff line number Diff line
@@ -313,6 +313,24 @@ subclass::

    For an example see the section `Adding custom validation to the admin`_.

    .. admonition:: Note

        If your ``ModelForm`` and ``ModelAdmin`` both define an ``exclude``
        option then ``ModelAdmin`` takes precedence::

            class PersonForm(forms.ModelForm):

                class Meta:
                    model = Person
                    exclude = ['name']

            class PersonAdmin(admin.ModelAdmin):
                exclude = ['age']
                form = PersonForm

        In the above example, the "age" field will be excluded but the "name"
        field will be included in the generated form.

.. attribute:: ModelAdmin.formfield_overrides

    This provides a quick-and-dirty way to override some of the
+93 −0
Original line number Diff line number Diff line
@@ -120,6 +120,99 @@ class ModelAdminTests(TestCase):
        self.assertEqual(ma.get_form(request).base_fields.keys(),
            ['name'])

    def test_custom_form_meta_exclude_with_readonly(self):
        """
        Ensure that the custom ModelForm's `Meta.exclude` is respected when
        used in conjunction with `ModelAdmin.readonly_fields` and when no
        `ModelAdmin.exclude` is defined.
        Refs #14496.
        """
        # First, with `ModelAdmin` -----------------------

        class AdminBandForm(forms.ModelForm):

            class Meta:
                model = Band
                exclude = ['bio']

        class BandAdmin(ModelAdmin):
            readonly_fields = ['name']
            form = AdminBandForm

        ma = BandAdmin(Band, self.site)
        self.assertEqual(ma.get_form(request).base_fields.keys(),
            ['sign_date',])

        # Then, with `InlineModelAdmin`  -----------------

        class AdminConcertForm(forms.ModelForm):

            class Meta:
                model = Concert
                exclude = ['day']

        class ConcertInline(TabularInline):
            readonly_fields = ['transport']
            form = AdminConcertForm
            fk_name = 'main_band'
            model = Concert

        class BandAdmin(ModelAdmin):
            inlines = [
                ConcertInline
            ]

        ma = BandAdmin(Band, self.site)
        self.assertEqual(
            list(ma.get_formsets(request))[0]().forms[0].fields.keys(),
            ['main_band', 'opening_band', 'id', 'DELETE',])

    def test_custom_form_meta_exclude(self):
        """
        Ensure that the custom ModelForm's `Meta.exclude` is overridden if
        `ModelAdmin.exclude` or `InlineModelAdmin.exclude` are defined.
        Refs #14496.
        """
        # First, with `ModelAdmin` -----------------------

        class AdminBandForm(forms.ModelForm):

            class Meta:
                model = Band
                exclude = ['bio']

        class BandAdmin(ModelAdmin):
            exclude = ['name']
            form = AdminBandForm

        ma = BandAdmin(Band, self.site)
        self.assertEqual(ma.get_form(request).base_fields.keys(),
            ['bio', 'sign_date',])

        # Then, with `InlineModelAdmin`  -----------------

        class AdminConcertForm(forms.ModelForm):

            class Meta:
                model = Concert
                exclude = ['day']

        class ConcertInline(TabularInline):
            exclude = ['transport']
            form = AdminConcertForm
            fk_name = 'main_band'
            model = Concert

        class BandAdmin(ModelAdmin):
            inlines = [
                ConcertInline
            ]

        ma = BandAdmin(Band, self.site)
        self.assertEqual(
            list(ma.get_formsets(request))[0]().forms[0].fields.keys(),
            ['main_band', 'opening_band', 'day', 'id', 'DELETE',])

    def test_custom_form_validation(self):
        # If we specify a form, it should use it allowing custom validation to work
        # properly. This won't, however, break any of the admin widgets or media.