diff --git a/docs/changelog.rst b/docs/changelog.rst index 60efb9c..9a79fdf 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -4,6 +4,7 @@ Changelog v4.2.0 (2025-12-01) ------------------- +* Fixed `Show full admin context (breadcrumb and logout nav) in model type selection admin form `_ * Fixed `Issue with Autocomplete Fields in StackedPolymorphicInline.Child Inline `_ * Support Python 3.14 and Django 6.0, drop support for EOL python 3.9, Django 3.2, 4.0, 4.1 and 5.0. * `Modernized package management with new build, test, docs tooling and improved CI diff --git a/src/polymorphic/admin/parentadmin.py b/src/polymorphic/admin/parentadmin.py index 98ec509..2b28d16 100644 --- a/src/polymorphic/admin/parentadmin.py +++ b/src/polymorphic/admin/parentadmin.py @@ -345,6 +345,7 @@ class PolymorphicParentModelAdmin(admin.ModelAdmin): "opts": opts, "add": True, "save_on_top": self.save_on_top, + **self.admin_site.each_context(request), } ) @@ -356,7 +357,7 @@ class PolymorphicParentModelAdmin(admin.ModelAdmin): ] request.current_app = self.admin_site.name - return TemplateResponse(request, templates, context) + return self.admin_site.admin_view(TemplateResponse)(request, templates, context) @property def change_list_template(self): diff --git a/src/polymorphic/tests/admin.py b/src/polymorphic/tests/admin.py index 5fe98fa..bcb9e83 100644 --- a/src/polymorphic/tests/admin.py +++ b/src/polymorphic/tests/admin.py @@ -1,6 +1,33 @@ -from django.contrib.admin import register, ModelAdmin -from polymorphic.admin import StackedPolymorphicInline, PolymorphicInlineSupportMixin -from polymorphic.tests.models import PlainA, InlineModelA, InlineModelB, InlineParent +from django.contrib.admin import register, ModelAdmin, site as admin_site +from polymorphic.admin import ( + StackedPolymorphicInline, + PolymorphicInlineSupportMixin, + PolymorphicChildModelAdmin, + PolymorphicChildModelFilter, + PolymorphicParentModelAdmin, +) + +from polymorphic.tests.models import ( + PlainA, + Model2A, + Model2B, + Model2C, + Model2D, + InlineModelA, + InlineModelB, + InlineParent, +) + + +@register(Model2A) +class Model2Admin(PolymorphicParentModelAdmin): + list_filter = (PolymorphicChildModelFilter,) + child_models = (Model2B, Model2C, Model2D) + + +admin_site.register(Model2B, PolymorphicChildModelAdmin) +admin_site.register(Model2C, PolymorphicChildModelAdmin) +admin_site.register(Model2D, PolymorphicChildModelAdmin) @register(PlainA) diff --git a/src/polymorphic/tests/test_admin.py b/src/polymorphic/tests/test_admin.py index b289bb0..444eb83 100644 --- a/src/polymorphic/tests/test_admin.py +++ b/src/polymorphic/tests/test_admin.py @@ -278,3 +278,88 @@ class StackedInlineTests(_GenericAdminFormTest): suggestions = self.page.locator("ul.select2-results__options > li").all_inner_texts() assert suggestions == ["Brian"] + + +class PolymorphicFormTests(_GenericAdminFormTest): + def setUp(self): + Model2A.objects.all().delete() + super().setUp() + + def tearDown(self): + Model2A.objects.all().delete() + super().tearDown() + + def test_admin_polymorphic_add(self): + model2b_ct = ContentType.objects.get_for_model(Model2B) + model2c_ct = ContentType.objects.get_for_model(Model2C) + model2d_ct = ContentType.objects.get_for_model(Model2D) + + for model_type, fields in [ + ( + model2b_ct, + { + "field1": "2B1", + "field2": "2B2", + }, + ), + ( + model2c_ct, + { + "field1": "2C1", + "field2": "2C2", + "field3": "2C3", + }, + ), + ( + model2d_ct, + { + "field1": "2D1", + "field2": "2D2", + "field3": "2D3", + "field4": "2D4", + }, + ), + ]: + self.page.goto(self.add_url(Model2A)) + + # https://github.com/jazzband/django-polymorphic/pull/580 + expect(self.page.locator("div.breadcrumbs")).to_have_count(1) + expect(self.page.locator("form#logout-form")).to_have_count(1) + + self.page.locator(f"input[type=radio][value='{model_type.pk}']").check() + with self.page.expect_navigation(timeout=10000) as nav_info: + self.page.click("input[name='_save']") + + response = nav_info.value + assert response.status < 400 + + for field, value in fields.items(): + self.page.fill(f"input[name='{field}']", value) + + with self.page.expect_navigation(timeout=10000) as nav_info: + self.page.click("input[name='_save']") + + response = nav_info.value + assert response.status < 400 + + assert Model2A.objects.count() == 3 + + object_ids = [int(oid) for oid in self.get_object_ids(Model2A)] + + assert len(object_ids) == 3 + + assert Model2B.objects.first().pk in object_ids + assert Model2C.objects.first().pk in object_ids + assert Model2D.objects.first().pk in object_ids + + assert Model2B.objects.first().field1 == "2B1" + assert Model2B.objects.first().field2 == "2B2" + + assert Model2C.objects.first().field1 == "2C1" + assert Model2C.objects.first().field2 == "2C2" + assert Model2C.objects.first().field3 == "2C3" + + assert Model2D.objects.first().field1 == "2D1" + assert Model2D.objects.first().field2 == "2D2" + assert Model2D.objects.first().field3 == "2D3" + assert Model2D.objects.first().field4 == "2D4"