From fedef7b2c6e80e1e58b028cf9c03267a8950bf6f Mon Sep 17 00:00:00 2001 From: jpic Date: Sat, 6 Jun 2015 03:29:07 +0200 Subject: [PATCH] Fixed #24908 -- Fixed duplicate readonly field rendering. ModelAdmin added readonly_fields to exclude, but would not undeclare them if they were overridden. --- django/contrib/admin/options.py | 13 +++++++++++-- tests/modeladmin/tests.py | 31 +++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 2 deletions(-) diff --git a/django/contrib/admin/options.py b/django/contrib/admin/options.py index 9534fad6f4..3232e8044d 100644 --- a/django/contrib/admin/options.py +++ b/django/contrib/admin/options.py @@ -605,7 +605,8 @@ class ModelAdmin(BaseModelAdmin): exclude = [] else: exclude = list(self.exclude) - exclude.extend(self.get_readonly_fields(request, obj)) + readonly_fields = self.get_readonly_fields(request, obj) + exclude.extend(readonly_fields) 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. @@ -613,8 +614,16 @@ class ModelAdmin(BaseModelAdmin): # if exclude is an empty list we pass None to be consistent with the # default on modelform_factory exclude = exclude or None + + # Remove declared form fields which are in readonly_fields. + new_attrs = OrderedDict( + (f, None) for f in readonly_fields + if f in self.form.declared_fields + ) + form = type(self.form.__name__, (self.form,), new_attrs) + defaults = { - "form": self.form, + "form": form, "fields": fields, "exclude": exclude, "formfield_callback": partial(self.formfield_for_dbfield, request=request), diff --git a/tests/modeladmin/tests.py b/tests/modeladmin/tests.py index 14ac030ec9..918daffe3e 100644 --- a/tests/modeladmin/tests.py +++ b/tests/modeladmin/tests.py @@ -221,6 +221,37 @@ class ModelAdminTests(TestCase): list(list(ma.get_formsets_with_inlines(request))[0][0]().forms[0].fields), ['main_band', 'opening_band', 'id', 'DELETE']) + def test_custom_formfield_override_readonly(self): + class AdminBandForm(forms.ModelForm): + name = forms.CharField() + + class Meta: + exclude = tuple() + model = Band + + class BandAdmin(ModelAdmin): + form = AdminBandForm + readonly_fields = ['name'] + + ma = BandAdmin(Band, self.site) + + # `name` shouldn't appear in base_fields because it's part of + # readonly_fields. + self.assertEqual( + list(ma.get_form(request).base_fields), + ['bio', 'sign_date'] + ) + # But it should appear in get_fields()/fieldsets() so it can be + # displayed as read-only. + self.assertEqual( + list(ma.get_fields(request)), + ['bio', 'sign_date', 'name'] + ) + self.assertEqual( + list(ma.get_fieldsets(request)), + [(None, {'fields': ['bio', 'sign_date', 'name']})] + ) + def test_custom_form_meta_exclude(self): """ Ensure that the custom ModelForm's `Meta.exclude` is overridden if