diff --git a/django/contrib/admin/helpers.py b/django/contrib/admin/helpers.py index a4aa8e40e3..d28a382814 100644 --- a/django/contrib/admin/helpers.py +++ b/django/contrib/admin/helpers.py @@ -509,6 +509,11 @@ class InlineAdminForm(AdminForm): # Auto fields are editable, so check for auto or non-editable pk. self.form._meta.model._meta.auto_field or not self.form._meta.model._meta.pk.editable + # The pk can be editable, but excluded from the inline. + or ( + self.form._meta.exclude + and self.form._meta.model._meta.pk.name in self.form._meta.exclude + ) or # Also search any parents for an auto field. (The pk info is # propagated to child models so that does not need to be checked diff --git a/tests/admin_inlines/admin.py b/tests/admin_inlines/admin.py index 3cdaee22df..578142d192 100644 --- a/tests/admin_inlines/admin.py +++ b/tests/admin_inlines/admin.py @@ -57,6 +57,8 @@ from .models import ( Teacher, Title, TitleCollection, + UUIDChild, + UUIDParent, ) site = admin.AdminSite(name="admin") @@ -471,6 +473,16 @@ class ShowInlineChildInline(admin.StackedInline): model = ShowInlineChild +class UUIDChildInline(admin.StackedInline): + model = UUIDChild + exclude = ("id",) + + +class UUIDParentModelAdmin(admin.ModelAdmin): + model = UUIDParent + inlines = [UUIDChildInline] + + class ShowInlineParentAdmin(admin.ModelAdmin): def get_inlines(self, request, obj): if obj is not None and obj.show_inlines: @@ -513,6 +525,7 @@ site.register(CourseProxy, ClassAdminStackedVertical) site.register(CourseProxy1, ClassAdminTabularVertical) site.register(CourseProxy2, ClassAdminTabularHorizontal) site.register(ShowInlineParent, ShowInlineParentAdmin) +site.register(UUIDParent, UUIDParentModelAdmin) # 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 5a85556a55..64aaca8d14 100644 --- a/tests/admin_inlines/models.py +++ b/tests/admin_inlines/models.py @@ -3,6 +3,7 @@ Testing of admin inline formsets. """ import random +import uuid from django.contrib.contenttypes.fields import GenericForeignKey from django.contrib.contenttypes.models import ContentType @@ -399,3 +400,13 @@ class BothVerboseNameProfile(Profile): class Meta: verbose_name = "Model with both - name" verbose_name_plural = "Model with both - plural name" + + +class UUIDParent(models.Model): + pass + + +class UUIDChild(models.Model): + id = models.UUIDField(default=uuid.uuid4, primary_key=True) + title = models.CharField(max_length=128) + parent = models.ForeignKey(UUIDParent, on_delete=models.CASCADE) diff --git a/tests/admin_inlines/tests.py b/tests/admin_inlines/tests.py index dee703825d..25512aede4 100644 --- a/tests/admin_inlines/tests.py +++ b/tests/admin_inlines/tests.py @@ -44,6 +44,8 @@ from .models import ( SomeChildModel, SomeParentModel, Teacher, + UUIDChild, + UUIDParent, VerboseNamePluralProfile, VerboseNameProfile, ) @@ -115,6 +117,19 @@ class TestInline(TestDataMixin, TestCase): ) self.assertContains(response, "") + def test_excluded_id_for_inlines_uses_hidden_field(self): + parent = UUIDParent.objects.create() + child = UUIDChild.objects.create(title="foo", parent=parent) + response = self.client.get( + reverse("admin:admin_inlines_uuidparent_change", args=(parent.id,)) + ) + self.assertContains( + response, + f'', + html=True, + ) + def test_many_to_many_inlines(self): "Autogenerated many-to-many inlines are displayed correctly (#13407)" response = self.client.get(reverse("admin:admin_inlines_author_add"))