diff --git a/django/contrib/admin/helpers.py b/django/contrib/admin/helpers.py index bf80ac5d48ea8..4f6bbe51e4e66 100644 --- a/django/contrib/admin/helpers.py +++ b/django/contrib/admin/helpers.py @@ -136,7 +136,6 @@ def errors(self): class AdminReadonlyField(object): def __init__(self, form, field, is_first, model_admin=None): - label = label_for_field(field, form._meta.model, model_admin) # Make self.field look a little bit like a field. This means that # {{ field.name }} must be a useful class name to identify the field. # For convenience, store other field-related data here too. @@ -144,11 +143,22 @@ def __init__(self, form, field, is_first, model_admin=None): class_name = field.__name__ if field.__name__ != '' else '' else: class_name = field + + if form._meta.labels and class_name in form._meta.labels: + label = form._meta.labels[class_name] + else: + label = label_for_field(field, form._meta.model, model_admin) + + if form._meta.help_texts and class_name in form._meta.help_texts: + help_text = form._meta.help_texts[class_name] + else: + help_text = help_text_for_field(class_name, form._meta.model) + self.field = { 'name': class_name, 'label': label, + 'help_text': help_text, 'field': field, - 'help_text': help_text_for_field(class_name, form._meta.model) } self.form = form self.model_admin = model_admin diff --git a/tests/admin_views/admin.py b/tests/admin_views/admin.py index a392daf293871..cd9ce2bf17b87 100644 --- a/tests/admin_views/admin.py +++ b/tests/admin_views/admin.py @@ -35,7 +35,7 @@ UnchangeableObject, UserMessenger, Simple, Choice, ShortMessage, Telegram, FilteredManager, EmptyModelHidden, EmptyModelVisible, EmptyModelMixin, State, City, Restaurant, Worker, ParentWithDependentChildren, - DependentChild, StumpJoke) + DependentChild, StumpJoke, FieldOverridePost) def callable_year(dt_value): @@ -435,6 +435,22 @@ def multiline_html(self, instance): value.short_description = 'Value in $US' +class FieldOverridePostForm(forms.ModelForm): + model = FieldOverridePost + + class Meta: + help_texts = { + 'posted': 'Overridden help text for the date', + } + labels = { + 'public': 'Overridden public label', + } + + +class FieldOverridePostAdmin(PostAdmin): + form = FieldOverridePostForm + + class CustomChangeList(ChangeList): def get_queryset(self, request): return self.root_queryset.filter(pk=9999) # Does not exist @@ -833,6 +849,7 @@ def get_changeform_initial_data(self, request): site.register(Collector, CollectorAdmin) site.register(Category, CategoryAdmin) site.register(Post, PostAdmin) +site.register(FieldOverridePost, FieldOverridePostAdmin) site.register(Gadget, GadgetAdmin) site.register(Villain) site.register(SuperVillain) diff --git a/tests/admin_views/models.py b/tests/admin_views/models.py index c729f6b90a9d9..fb72b12717bfe 100644 --- a/tests/admin_views/models.py +++ b/tests/admin_views/models.py @@ -438,6 +438,13 @@ def awesomeness_level(self): return "Very awesome." +# Proxy model to test overridden fields attrs on Post model so as not to +# interfere with other tests. +class FieldOverridePost(Post): + class Meta: + proxy = True + + @python_2_unicode_compatible class Gadget(models.Model): name = models.CharField(max_length=100) diff --git a/tests/admin_views/tests.py b/tests/admin_views/tests.py index a88117fa6f882..39a79c06140d6 100644 --- a/tests/admin_views/tests.py +++ b/tests/admin_views/tests.py @@ -52,7 +52,7 @@ Report, MainPrepopulated, RelatedPrepopulated, UnorderedObject, Simple, UndeletableObject, UnchangeableObject, Choice, ShortMessage, Telegram, Pizza, Topping, FilteredManager, City, Restaurant, Worker, - ParentWithDependentChildren, Character) + ParentWithDependentChildren, Character, FieldOverridePost) from .admin import site, site2, CityAdmin @@ -3693,6 +3693,18 @@ def test_readonly_backwards_ref(self): response = self.client.get('/test_admin/admin/admin_views/topping/add/') self.assertEqual(response.status_code, 200) + def test_readonly_field_overrides(self): + """ + Regression test for #22087 - ModelForm Meta overrides are ignored by + AdminReadonlyField + """ + p = FieldOverridePost.objects.create(title="Test Post", content="Test Content") + response = self.client.get('/test_admin/admin/admin_views/fieldoverridepost/%d/' % p.pk) + self.assertEqual(response.status_code, 200) + self.assertContains(response, '

Overridden help text for the date

') + self.assertContains(response, '', html=True) + self.assertNotContains(response, "Some help text for the date (with unicode ŠĐĆŽćžšđ)") + @override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',)) class LimitChoicesToInAdminTest(TestCase):