Skip to content

Commit 262ca22

Browse files
committed
Fixed 2259 -- Made manually defined PKs readonly in admin inlines.
1 parent 25fae5e commit 262ca22

File tree

3 files changed

+42
-20
lines changed

3 files changed

+42
-20
lines changed

django/contrib/admin/options.py

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -414,27 +414,37 @@ def get_ordering(self, request):
414414
"""
415415
return self.ordering or () # otherwise we might try to *None, which is bad ;)
416416

417-
def get_readonly_fields(self, request, obj=None):
417+
def _get_readonly_manual_pk_fields(self, obj):
418418
"""
419-
Hook for specifying custom readonly fields.
419+
Returns a list of primary key field names that should be readonly if:
420+
- we're editing an existing object,
421+
- the PK was manually defined (not auto-created),
422+
- and it's not already in self.readonly_fields.
423+
Supports both single and composite PKs.
420424
"""
421-
readonly = self.readonly_fields
422-
pk_field = self.model._meta.pk
425+
if not obj:
426+
return []
427+
428+
readonly = []
429+
# Supports forward compatibility with composite primary keys
430+
pk_fields = self.model._meta.pk_fields
431+
432+
for pk in pk_fields:
433+
if (
434+
pk.editable
435+
and not pk.auto_created
436+
and pk.name not in self.readonly_fields
437+
):
438+
readonly.append(pk.name)
423439

424-
# Only add PK to readonly if:
425-
# - editing an existing object
426-
# - PK is not auto-created
427-
# - it's not already in the readonly list
428-
# - and this is NOT an inline admin (which has `parent_model`)
429-
if (
430-
obj is not None
431-
and not pk_field.auto_created
432-
and pk_field.name not in readonly
433-
and not hasattr(self, "parent_model")
434-
):
435-
readonly = readonly + (pk_field.name,)
436440
return readonly
437441

442+
def get_readonly_fields(self, request, obj=None):
443+
"""
444+
Hook for specifying custom readonly fields.
445+
"""
446+
return list(self.readonly_fields) + self._get_readonly_manual_pk_fields(obj)
447+
438448
def get_prepopulated_fields(self, request, obj=None):
439449
"""
440450
Hook for specifying custom prepopulated fields.

django/contrib/admin/static/admin/css/base.css

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -109,10 +109,6 @@ body {
109109
background: var(--body-bg);
110110
}
111111

112-
label {
113-
font-size: 1rem;
114-
}
115-
116112
/* LINKS */
117113

118114
a:link, a:visited {

tests/admin_inlines/tests.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
Child,
2020
ChildModel1,
2121
ChildModel2,
22+
EditablePKBook,
2223
ExtraTerrestrial,
2324
Fashionista,
2425
FootNote,
@@ -708,6 +709,21 @@ def test_inline_editable_pk(self):
708709
count=1,
709710
)
710711

712+
def test_inline_manual_pk_is_readonly_when_editing(self):
713+
author = Author.objects.create(name="Jane Austen")
714+
EditablePKBook.objects.create(
715+
author=author, manual_pk=101, title="Pride and Prejudice"
716+
)
717+
718+
response = self.client.get(
719+
reverse("admin:admin_inlines_author_change", args=[author.pk])
720+
)
721+
722+
self.assertContains(
723+
response, 'name="editablepkbook_set-0-manual_pk"', html=False
724+
)
725+
self.assertContains(response, "readonly", html=False)
726+
711727
def test_stacked_inline_edit_form_contains_has_original_class(self):
712728
holder = Holder.objects.create(dummy=1)
713729
holder.inner_set.create(dummy=1)

0 commit comments

Comments
 (0)