Skip to content

Fixed #2259 -- Made manually specified primary keys readonly in the admin. #19675

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 6 commits into
base: main
Choose a base branch
from

Conversation

Jkhall81
Copy link
Contributor

@Jkhall81 Jkhall81 commented Jul 26, 2025

Trac ticket number

https://code.djangoproject.com/ticket/2259

Branch description

When editing a model instance in the Django admin, manually specified primary key fields (e.g. CharField(primary_key=True)) should not be editable. Without this fix, users can mistakenly change a model's PK, which can cause errors or lead to duplicate records.

This patch updates BaseModelAdmin.get_readonly_fields() to automatically mark such PKs as readonly if they are not auto-generated and not already in the readonly fields.

This change was verified against existing admin inline tests (test_char_pk_inline, test_integer_pk_inline), which initially failed due to the patch, and the logic was adjusted to restore their passing state.

Checklist

  • [x ] This PR targets the main branch.
  • [x ] The commit message is written in past tense, mentions the ticket number, and ends with a period.
  • [x ] I have checked the "Has patch" ticket flag in the Trac system.
  • I have added or updated relevant tests.
  • I have added or updated relevant docs, including release notes if applicable.
  • I have attached screenshots in both light and dark modes for any UI changes.

@Jkhall81 Jkhall81 force-pushed the 2259-readonly-manual-pk-admin branch from 094817a to 7a78c93 Compare July 26, 2025 03:24
@@ -418,7 +418,22 @@ def get_readonly_fields(self, request, obj=None):
"""
Hook for specifying custom readonly fields.
"""
return self.readonly_fields
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It works on my test project, and it should not affect existing overrides. It also provides the option to fall back to the initial behavior:

def get_readonly_fields(self, request, obj=None):
    return self.readonly_fields


# Only add PK to readonly if:
# - editing an existing object
# - PK is not auto-created
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any particular reason why an auto_created pk needs to be excluded?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

auto_created pks are already not editable in the admin.

@sarahboyce sarahboyce added the selenium Apply to have Selenium tests run on a PR label Jul 29, 2025
Copy link
Contributor

@sarahboyce sarahboyce left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the PR
This doesn't seem to be working fully for inlines.
Given the models:

class State(models.Model):
    label = models.CharField(max_length=255)


class StatePKModel(models.Model):
    id = models.IntegerField(primary_key=True)
    state = models.ForeignKey(State, related_name="pk_state_model", on_delete=models.CASCADE)

and admin

class StatePKModelInline(admin.TabularInline):
    model = StatePKModel


@admin.register(State)
class StateAdmin(admin.ModelAdmin):
    inlines = [StatePKModelInline,]

I can edit the id in the inline
Screenshot from 2025-07-29 10-43-02

Comment on lines 112 to 115
label {
font-size: 1rem;
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change looks to be unrelated to the ticket

Copy link
Contributor Author

@Jkhall81 Jkhall81 Jul 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That was my bad. Fixed this css thing (was from a year old PR). Also, I confirmed with a test that manually defined Pks are readonly in inlines when editing existing objects.

@@ -418,7 +418,22 @@ def get_readonly_fields(self, request, obj=None):
"""
Hook for specifying custom readonly fields.
"""
return self.readonly_fields
readonly = self.readonly_fields
pk_field = self.model._meta.pk
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should account for composite primary keys now and use _meta.pk_fields.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! Updated to use _meta.pk_fields for future composite PK support, with fallback to _meta.pk for compatibility.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated to use _meta.pk_fields for future composite PK support, with fallback to _meta.pk for compatibility.

No need for a fallback, see the docs.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fallback removed.

@Jkhall81 Jkhall81 force-pushed the 2259-readonly-manual-pk-admin branch from 893d265 to 262ca22 Compare July 29, 2025 18:36
@Jkhall81 Jkhall81 force-pushed the 2259-readonly-manual-pk-admin branch from 262ca22 to 2153973 Compare July 29, 2025 18:50
@Jkhall81 Jkhall81 requested a review from sarahboyce July 30, 2025 12:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
selenium Apply to have Selenium tests run on a PR
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants