From 51badee74914726d536f487305c200023cb33619 Mon Sep 17 00:00:00 2001 From: Jkhall81 Date: Sun, 24 Mar 2024 11:06:35 -0700 Subject: [PATCH 1/6] Added CSS rule for label element making font-size 1rem to address automatic zoom issue in mobile Safari. --- django/contrib/admin/static/admin/css/base.css | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/django/contrib/admin/static/admin/css/base.css b/django/contrib/admin/static/admin/css/base.css index f9f47c8592eb..1b0bfe1d67b3 100644 --- a/django/contrib/admin/static/admin/css/base.css +++ b/django/contrib/admin/static/admin/css/base.css @@ -101,6 +101,10 @@ body { background: var(--body-bg); } +label { + font-size: 1rem; +} + /* LINKS */ a:link, a:visited { From 89b67a324576e95e7ea6c052ab3b58f9a5a49f6c Mon Sep 17 00:00:00 2001 From: Jkhall81 Date: Fri, 25 Jul 2025 19:37:35 -0700 Subject: [PATCH 2/6] Fixed #2259 -- Made manually defined primary keys readonly in admin change forms. --- django/contrib/admin/options.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/django/contrib/admin/options.py b/django/contrib/admin/options.py index 6c202c8e613c..500efe80feda 100644 --- a/django/contrib/admin/options.py +++ b/django/contrib/admin/options.py @@ -418,7 +418,12 @@ def get_readonly_fields(self, request, obj=None): """ Hook for specifying custom readonly fields. """ - return self.readonly_fields + readonly = super().get_readonly_fields(request, obj) + pk_field = self.model._meta.pk + + if obj is not None and not pk_field.auto_created and pk_field.name not in readonly: + readonly = readonly + (pk_field.name,) + return readonly def get_prepopulated_fields(self, request, obj=None): """ From 7a78c93ccc6ebd936054df8fbc789077991eb6b1 Mon Sep 17 00:00:00 2001 From: Jkhall81 Date: Fri, 25 Jul 2025 19:47:38 -0700 Subject: [PATCH 3/6] Fixed #2259 -- Made non-auto fields readonly in admin A --- django/contrib/admin/options.py | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/django/contrib/admin/options.py b/django/contrib/admin/options.py index 500efe80feda..4d74a13ea200 100644 --- a/django/contrib/admin/options.py +++ b/django/contrib/admin/options.py @@ -418,11 +418,21 @@ def get_readonly_fields(self, request, obj=None): """ Hook for specifying custom readonly fields. """ - readonly = super().get_readonly_fields(request, obj) + readonly = self.readonly_fields pk_field = self.model._meta.pk - if obj is not None and not pk_field.auto_created and pk_field.name not in readonly: - readonly = readonly + (pk_field.name,) + # Only add PK to readonly if: + # - editing an existing object + # - PK is not auto-created + # - it's not already in the readonly list + # - and this is NOT an inline admin (which has `parent_model`) + if ( + obj is not None + and not pk_field.auto_created + and pk_field.name not in readonly + and not hasattr(self, "parent_model") + ): + readonly = readonly + (pk_field.name,) return readonly def get_prepopulated_fields(self, request, obj=None): From 25fae5e25c875a98118e7cc354acf7fe6ef8f0d2 Mon Sep 17 00:00:00 2001 From: Jkhall81 Date: Fri, 25 Jul 2025 20:47:52 -0700 Subject: [PATCH 4/6] Adjusted test to match readonly PK behavior in admin (#2259) --- tests/admin_custom_urls/tests.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/admin_custom_urls/tests.py b/tests/admin_custom_urls/tests.py index d401976ebb40..6eda92b33b71 100644 --- a/tests/admin_custom_urls/tests.py +++ b/tests/admin_custom_urls/tests.py @@ -95,7 +95,11 @@ def test_admin_URLs_no_clash(self): ) response = self.client.get(url) self.assertContains(response, "Change action") - self.assertContains(response, 'value="path/to/html/document.html"') + self.assertContains( + response, + '
path/to/html/document.html
', + html=True, + ) def test_post_save_add_redirect(self): """ From 21539731a574d119a11fe83a502bd89a3450b92e Mon Sep 17 00:00:00 2001 From: jkhall81 Date: Tue, 29 Jul 2025 11:24:30 -0700 Subject: [PATCH 5/6] Fixed 2259 -- Made manually defined PKs readonly in admin inlines. --- django/contrib/admin/options.py | 41 +++++++++++-------- .../contrib/admin/static/admin/css/base.css | 4 -- tests/admin_inlines/tests.py | 16 ++++++++ 3 files changed, 41 insertions(+), 20 deletions(-) diff --git a/django/contrib/admin/options.py b/django/contrib/admin/options.py index 4d74a13ea200..0568604b4bab 100644 --- a/django/contrib/admin/options.py +++ b/django/contrib/admin/options.py @@ -414,27 +414,36 @@ def get_ordering(self, request): """ return self.ordering or () # otherwise we might try to *None, which is bad ;) - def get_readonly_fields(self, request, obj=None): + def _get_readonly_manual_pk_fields(self, obj): """ - Hook for specifying custom readonly fields. + Returns a list of primary key field names that should be readonly if: + - we're editing an existing object, + - the PK was manually defined (not auto-created), + - and it's not already in self.readonly_fields. + Supports both single and composite PKs. """ - readonly = self.readonly_fields - pk_field = self.model._meta.pk + if not obj or hasattr(self, "parent_model"): + return [] + + readonly = [] + pk_fields = self.model._meta.pk_fields + + for pk in pk_fields: + if ( + pk.editable + and not pk.auto_created + and pk.name not in self.readonly_fields + ): + readonly.append(pk.name) - # Only add PK to readonly if: - # - editing an existing object - # - PK is not auto-created - # - it's not already in the readonly list - # - and this is NOT an inline admin (which has `parent_model`) - if ( - obj is not None - and not pk_field.auto_created - and pk_field.name not in readonly - and not hasattr(self, "parent_model") - ): - readonly = readonly + (pk_field.name,) return readonly + def get_readonly_fields(self, request, obj=None): + """ + Hook for specifying custom readonly fields. + """ + return list(self.readonly_fields) + self._get_readonly_manual_pk_fields(obj) + def get_prepopulated_fields(self, request, obj=None): """ Hook for specifying custom prepopulated fields. diff --git a/django/contrib/admin/static/admin/css/base.css b/django/contrib/admin/static/admin/css/base.css index 8de2f214d81a..e2be6a78fe96 100644 --- a/django/contrib/admin/static/admin/css/base.css +++ b/django/contrib/admin/static/admin/css/base.css @@ -109,10 +109,6 @@ body { background: var(--body-bg); } -label { - font-size: 1rem; -} - /* LINKS */ a:link, a:visited { diff --git a/tests/admin_inlines/tests.py b/tests/admin_inlines/tests.py index 0b7308995025..bc0978138875 100644 --- a/tests/admin_inlines/tests.py +++ b/tests/admin_inlines/tests.py @@ -19,6 +19,7 @@ Child, ChildModel1, ChildModel2, + EditablePKBook, ExtraTerrestrial, Fashionista, FootNote, @@ -708,6 +709,21 @@ def test_inline_editable_pk(self): count=1, ) + def test_inline_manual_pk_is_readonly_when_editing(self): + author = Author.objects.create(name="Jane Austen") + EditablePKBook.objects.create( + author=author, manual_pk=101, title="Pride and Prejudice" + ) + + response = self.client.get( + reverse("admin:admin_inlines_author_change", args=[author.pk]) + ) + + self.assertContains( + response, 'name="editablepkbook_set-0-manual_pk"', html=False + ) + self.assertContains(response, "readonly", html=False) + def test_stacked_inline_edit_form_contains_has_original_class(self): holder = Holder.objects.create(dummy=1) holder.inner_set.create(dummy=1) From 208506640dd0bcde4c4197e45f5d92772b9a2bb4 Mon Sep 17 00:00:00 2001 From: Jkhall81 Date: Tue, 5 Aug 2025 06:49:52 -0700 Subject: [PATCH 6/6] Added get_readonly_fields override to InlineModelAdmin for manual PKs. --- django/contrib/admin/options.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/django/contrib/admin/options.py b/django/contrib/admin/options.py index 0568604b4bab..9a32d4e0d4f6 100644 --- a/django/contrib/admin/options.py +++ b/django/contrib/admin/options.py @@ -2573,6 +2573,18 @@ def has_view_permission(self, request, obj=None): return self._has_any_perms_for_target_model(request, ["view", "change"]) return super().has_view_permission(request) + def get_readonly_fields(self, request, obj=None): + """ + Make manually specified (non-auto-created) primary key fields readonly + when editing an existing inline object. + """ + readonly = list(self.readonly_fields) + if obj: + for pk in self.model._meta.pk_fields: + if pk.editable and not pk.auto_created and pk.name not in readonly: + readonly.append(pk.name) + return readonly + class StackedInline(InlineModelAdmin): template = "admin/edit_inline/stacked.html"