From 2f0f30f973363a59c20f204f9351724fb2ce7327 Mon Sep 17 00:00:00 2001 From: Hasan Ramezani Date: Thu, 16 Sep 2021 10:34:10 +0200 Subject: [PATCH] Fixed #33111 -- Fixed passing object to ModelAdmin.get_inlines() when editing in admin change view. ModelAdmin.get_inlines() should get an unmutated object when creating formsets during POST request. --- django/contrib/admin/options.py | 6 +++++- tests/admin_inlines/admin.py | 17 +++++++++++++++-- tests/admin_inlines/models.py | 6 ++++++ tests/admin_inlines/tests.py | 19 +++++++++++++++++-- 4 files changed, 43 insertions(+), 5 deletions(-) diff --git a/django/contrib/admin/options.py b/django/contrib/admin/options.py index 1e5fab917e..7b2b893c10 100644 --- a/django/contrib/admin/options.py +++ b/django/contrib/admin/options.py @@ -1585,12 +1585,16 @@ class ModelAdmin(BaseModelAdmin): ) if request.method == 'POST': form = ModelForm(request.POST, request.FILES, instance=obj) + formsets, inline_instances = self._create_formsets( + request, + form.instance if add else obj, + change=not add, + ) form_validated = form.is_valid() if form_validated: new_object = self.save_form(request, form, change=not add) else: new_object = form.instance - formsets, inline_instances = self._create_formsets(request, new_object, change=not add) if all_valid(formsets) and form_validated: self.save_model(request, new_object, form, not add) self.save_related(request, form, formsets, not add) diff --git a/tests/admin_inlines/admin.py b/tests/admin_inlines/admin.py index c444526241..116556db7e 100644 --- a/tests/admin_inlines/admin.py +++ b/tests/admin_inlines/admin.py @@ -11,8 +11,9 @@ from .models import ( Inner4Tabular, Inner5Stacked, Inner5Tabular, NonAutoPKBook, NonAutoPKBookChild, Novel, NovelReadonlyChapter, OutfitItem, ParentModelWithCustomPk, Person, Poll, Profile, ProfileCollection, - Question, ReadOnlyInline, ShoppingWeakness, Sighting, SomeChildModel, - SomeParentModel, SottoCapo, Teacher, Title, TitleCollection, + Question, ReadOnlyInline, ShoppingWeakness, ShowInlineChild, + ShowInlineParent, Sighting, SomeChildModel, SomeParentModel, SottoCapo, + Teacher, Title, TitleCollection, ) site = admin.AdminSite(name="admin") @@ -371,6 +372,17 @@ class ChildHiddenFieldOnSingleLineStackedInline(admin.StackedInline): fields = ('name', 'position') +class ShowInlineChildInline(admin.StackedInline): + model = ShowInlineChild + + +class ShowInlineParentAdmin(admin.ModelAdmin): + def get_inlines(self, request, obj): + if obj is not None and obj.show_inlines: + return [ShowInlineChildInline] + return [] + + site.register(TitleCollection, inlines=[TitleInline]) # Test bug #12561 and #12778 # only ModelAdmin media @@ -402,6 +414,7 @@ site.register(Course, ClassAdminStackedHorizontal) site.register(CourseProxy, ClassAdminStackedVertical) site.register(CourseProxy1, ClassAdminTabularVertical) site.register(CourseProxy2, ClassAdminTabularHorizontal) +site.register(ShowInlineParent, ShowInlineParentAdmin) # Used to test hidden fields in tabular and stacked inlines. site2 = admin.AdminSite(name='tabular_inline_hidden_field_admin') site2.register(SomeParentModel, inlines=[ChildHiddenFieldTabularInline]) diff --git a/tests/admin_inlines/models.py b/tests/admin_inlines/models.py index 900eb34fca..6125e06c28 100644 --- a/tests/admin_inlines/models.py +++ b/tests/admin_inlines/models.py @@ -327,6 +327,12 @@ class CourseProxy2(Course): # Other models +class ShowInlineParent(models.Model): + show_inlines = models.BooleanField(default=False) + + +class ShowInlineChild(models.Model): + parent = models.ForeignKey(ShowInlineParent, on_delete=models.CASCADE) class ProfileCollection(models.Model): diff --git a/tests/admin_inlines/tests.py b/tests/admin_inlines/tests.py index c50ad8cf1d..1c8d6bd271 100644 --- a/tests/admin_inlines/tests.py +++ b/tests/admin_inlines/tests.py @@ -12,8 +12,8 @@ from .models import ( ChildModel1, ChildModel2, Fashionista, FootNote, Holder, Holder2, Holder3, Holder4, Inner, Inner2, Inner3, Inner4Stacked, Inner4Tabular, Novel, OutfitItem, Parent, ParentModelWithCustomPk, Person, Poll, Profile, - ProfileCollection, Question, Sighting, SomeChildModel, SomeParentModel, - Teacher, VerboseNamePluralProfile, VerboseNameProfile, + ProfileCollection, Question, ShowInlineParent, Sighting, SomeChildModel, + SomeParentModel, Teacher, VerboseNamePluralProfile, VerboseNameProfile, ) INLINE_CHANGELINK_HTML = 'class="inlinechangelink">Change' @@ -618,6 +618,21 @@ class TestInline(TestDataMixin, TestCase): self.assertContains(response, '

Author

', html=True) # Tabular. self.assertContains(response, '

Fashionista

', html=True) # Stacked. + def test_inlines_based_on_model_state(self): + parent = ShowInlineParent.objects.create(show_inlines=False) + data = { + 'show_inlines': 'on', + '_save': 'Save', + } + change_url = reverse( + 'admin:admin_inlines_showinlineparent_change', + args=(parent.id,), + ) + response = self.client.post(change_url, data) + self.assertEqual(response.status_code, 302) + parent.refresh_from_db() + self.assertIs(parent.show_inlines, True) + @override_settings(ROOT_URLCONF='admin_inlines.urls') class TestInlineMedia(TestDataMixin, TestCase):