#24377 closed Bug (fixed)
UUIDField as primary key breaks inline admins
Reported by: | Michael Angeletti | Owned by: | Tim Graham |
---|---|---|---|
Component: | Forms | Version: | dev |
Severity: | Release blocker | Keywords: | |
Cc: | Triage Stage: | Accepted | |
Has patch: | yes | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | no |
Easy pickings: | no | UI/UX: | no |
Description
class Parent(models.Model): uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) name = models.CharField(max_length=255) class Child(models.Model): uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) name = models.CharField(max_length=255) parent = models.ForeignKey(Parent)
class ChildInline(admin.TabularInline): model = Child class ParentAdmin(admin.ModelAdmin): inlines = [ChildInline]
Given the above models and admins:
If you attempt to create a Parent
, all the Child
fields will contain This field is required.
errors.
I think the issue here is default=uuid.uuid4
, and I think what's happening is, since the uuid4
value provided to the uuid
field on page load will differ from the uuid4
value provided when the form is posted, Django is considering the state as having been changed (resulting in validation).
I've set the Component as Forms, since this is most likely a form issue, rather than being admin-specific. I've also set the Severity to Release Blocker, since the last UUIDField
issue I encountered was marked as such, and since this sort of breaks the (probably) most common usage of UUIDField
. Please let me know if I should not set the Severity in the future.
Change History (13)
comment:1 by , 10 years ago
comment:2 by , 10 years ago
Version: | 1.8alpha1 → master |
---|
comment:4 by , 10 years ago
Triage Stage: | Unreviewed → Accepted |
---|
comment:5 by , 10 years ago
Owner: | changed from | to
---|---|
Status: | new → assigned |
There appear to be a couple problems here:
Since the use of UUIDField
as a primary key requires default=uuid.uuid4
, initializing an object sets its primary key in Model.__init__():
>>> p = Parent() >>> p.pk UUID('cde58b1c-4a5a-471a-901c-6e43847f0a0e')
That value is then used as the initial value for InlineForeignKeyField on the inlines. It needs to be ignored since it will be regenerated on subsequent requests since the field isn't editable and there's no way to preserve state across requests.
Secondly, the primary key on the inline uses an auto-generated value from the default
of the Child.uuid
field during the save request, but it's empty on the request for the initial form. This discrepancy causes Form.has_changed()
to return True
which triggers "required" validation on other fields, even on otherwise empty inlines.
I have a patch that seems to fix these issues and am working on adding tests.
comment:7 by , 10 years ago
Patch needs improvement: | set |
---|
Unfortunately, there is a failing test for a case involving model inheritance and editable primary keys.
comment:8 by , 10 years ago
@timgraham - I tried to debug this some and ended up down a deep rabbit hole.
I ended up possibly finding another bug. With stable/1.8.x (without your changes), given the example models, if you create a Child
instance for an existing Parent
, set max_num = 1
on the inline (just to avoid empty forms, since you only created 1 child), then visit the change view for that Parent
and save it, all should be well, but all is not well. The global Please correct the errors below.
appears at the top, but there are no errors. Removing the inline admin fixes that issue, and checking "delete" on the inline instance is not met with this issue, so the issue is certainly pertaining validation of the inline form or instance.
comment:9 by , 10 years ago
Patch needs improvement: | unset |
---|
Tests are passing on my pull request now.
comment:10 by , 10 years ago
@timgraham - see also #24391 (pertains my original theory of the cause of this, which now seems more relevant)
comment:11 by , 10 years ago
Resolution: | → fixed |
---|---|
Status: | assigned → closed |
Ok, so pertaining my crackpot theory of why this issue occurs, you'll have to make allowances because I'm sleep deprived. Obviously, the issue is not caused by Django somehow knowing the original GET version of the
uuid4
value when you POST the form over... stateless HTTP.