Loading django/contrib/admin/options.py +11 −2 Original line number Diff line number Diff line Loading @@ -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. Loading @@ -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), Loading tests/modeladmin/tests.py +31 −0 Original line number Diff line number Diff line Loading @@ -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 Loading Loading
django/contrib/admin/options.py +11 −2 Original line number Diff line number Diff line Loading @@ -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. Loading @@ -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), Loading
tests/modeladmin/tests.py +31 −0 Original line number Diff line number Diff line Loading @@ -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 Loading