Opened 10 years ago

Closed 10 years ago

Last modified 18 months ago

#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 Michael Angeletti, 10 years ago

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.

comment:2 by Michael Angeletti, 10 years ago

Version: 1.8alpha1master

comment:3 by Claude Paroz, 10 years ago

Could be a duplicate of #15665

comment:4 by Marc Tamlyn, 10 years ago

Triage Stage: UnreviewedAccepted

comment:5 by Tim Graham, 10 years ago

Owner: changed from nobody to Tim Graham
Status: newassigned

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:6 by Tim Graham, 10 years ago

Has patch: set

comment:7 by Tim Graham, 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 Michael Angeletti, 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 Tim Graham, 10 years ago

Patch needs improvement: unset

Tests are passing on my pull request now.

comment:10 by Michael Angeletti, 10 years ago

@timgraham - see also #24391 (pertains my original theory of the cause of this, which now seems more relevant)

comment:11 by Tim Graham <timograham@…>, 10 years ago

Resolution: fixed
Status: assignedclosed

In 1306cd1e8acfb13602ee8dc40993b2505cd7523b:

Fixed #24377 -- Fixed model inline formsets with primary key's that have defaults.

comment:12 by Tim Graham <timograham@…>, 10 years ago

In 41d5ed480cd55a71b739e779ca11f24caaa2b302:

[1.8.x] Fixed #24377 -- Fixed model inline formsets with primary key's that have defaults.

Backport of 1306cd1e8acfb13602ee8dc40993b2505cd7523b from master

comment:13 by Mariusz Felisiak <felisiak.mariusz@…>, 18 months ago

In b91d62c:

Refs #24377 -- Added assertions for model inlines with primary key that has a default.

This ensures that a model field default is ignored.

Note: See TracTickets for help on using tickets.
Back to Top