diff --git a/django/contrib/admin/helpers.py b/django/contrib/admin/helpers.py index 8bb3df7c43..5e8e0caf69 100644 --- a/django/contrib/admin/helpers.py +++ b/django/contrib/admin/helpers.py @@ -244,9 +244,10 @@ class InlineAdminFormSet: self.has_view_permission = has_view_permission def __iter__(self): - readonly_fields_for_editing = self.readonly_fields - if not self.has_change_permission: - readonly_fields_for_editing += flatten_fieldsets(self.fieldsets) + if self.has_change_permission: + readonly_fields_for_editing = self.readonly_fields + else: + readonly_fields_for_editing = self.readonly_fields + flatten_fieldsets(self.fieldsets) for form, original in zip(self.formset.initial_forms, self.formset.get_queryset()): view_on_site_url = self.opts.get_view_on_site_url(original) diff --git a/tests/admin_inlines/admin.py b/tests/admin_inlines/admin.py index a56704865d..7ac13ed3a4 100644 --- a/tests/admin_inlines/admin.py +++ b/tests/admin_inlines/admin.py @@ -7,9 +7,9 @@ from .models import ( Consigliere, EditablePKBook, ExtraTerrestrial, Fashionista, Holder, Holder2, Holder3, Holder4, Inner, Inner2, Inner3, Inner4Stacked, Inner4Tabular, NonAutoPKBook, NonAutoPKBookChild, Novel, - ParentModelWithCustomPk, Poll, Profile, ProfileCollection, Question, - ReadOnlyInline, ShoppingWeakness, Sighting, SomeChildModel, - SomeParentModel, SottoCapo, Title, TitleCollection, + NovelReadonlyChapter, ParentModelWithCustomPk, Poll, Profile, + ProfileCollection, Question, ReadOnlyInline, ShoppingWeakness, Sighting, + SomeChildModel, SomeParentModel, SottoCapo, Title, TitleCollection, ) site = admin.AdminSite(name="admin") @@ -153,6 +153,17 @@ class NovelAdmin(admin.ModelAdmin): inlines = [ChapterInline] +class ReadOnlyChapterInline(admin.TabularInline): + model = Chapter + + def has_change_permission(self, request, obj=None): + return False + + +class NovelReadonlyChapterAdmin(admin.ModelAdmin): + inlines = [ReadOnlyChapterInline] + + class ConsigliereInline(admin.TabularInline): model = Consigliere @@ -231,6 +242,7 @@ site.register(Holder3, inlines=[InnerInline3]) site.register(Poll, PollAdmin) site.register(Novel, NovelAdmin) +site.register(NovelReadonlyChapter, NovelReadonlyChapterAdmin) site.register(Fashionista, inlines=[InlineWeakness]) site.register(Holder4, Holder4Admin) site.register(Author, AuthorAdmin) diff --git a/tests/admin_inlines/models.py b/tests/admin_inlines/models.py index 94134660e5..cb1ec39ae5 100644 --- a/tests/admin_inlines/models.py +++ b/tests/admin_inlines/models.py @@ -159,6 +159,12 @@ class Novel(models.Model): name = models.CharField(max_length=40) +class NovelReadonlyChapter(Novel): + + class Meta: + proxy = True + + class Chapter(models.Model): name = models.CharField(max_length=40) novel = models.ForeignKey(Novel, models.CASCADE) diff --git a/tests/admin_inlines/tests.py b/tests/admin_inlines/tests.py index 1bf9b34e77..4ce744f4ef 100644 --- a/tests/admin_inlines/tests.py +++ b/tests/admin_inlines/tests.py @@ -447,6 +447,16 @@ class TestInline(TestDataMixin, TestCase): self.assertTrue(response.context['inline_admin_formset'].opts.has_registered_model) self.assertNotContains(response, INLINE_CHANGELINK_HTML) + def test_noneditable_inline_has_field_inputs(self): + """Inlines without change permission shows field inputs on add form.""" + response = self.client.get(reverse('admin:admin_inlines_novelreadonlychapter_add')) + self.assertContains( + response, + '', + html=True + ) + @override_settings(ROOT_URLCONF='admin_inlines.urls') class TestInlineMedia(TestDataMixin, TestCase):