#35393: InlineAdmin's are not possible with an editable UUIDField as primary 
key.
-------------------------------------+-------------------------------------
     Reporter:  Willem Van Onsem     |                    Owner:  Willem
                                     |  Van Onsem
         Type:  Bug                  |                   Status:  assigned
    Component:  contrib.admin        |                  Version:  5.0
     Severity:  Normal               |               Resolution:
     Keywords:                       |             Triage Stage:  Accepted
    Has patch:  1                    |      Needs documentation:  0
  Needs tests:  1                    |  Patch needs improvement:  0
Easy pickings:  0                    |                    UI/UX:  0
-------------------------------------+-------------------------------------
Changes (by Sarah Boyce):

 * needs_tests:  0 => 1
 * owner:  nobody => Willem Van Onsem
 * stage:  Unreviewed => Accepted
 * status:  new => assigned
 * summary:
     InlineAdmin's are *not* possible with an *editable* UUIDField as
     primary key.
     =>
     InlineAdmin's are not possible with an editable UUIDField as primary
     key.


Old description:

> This issue was reported on [https://stackoverflow.com/a/78362210/67579
> StackOverflow]: if we have a model with an **editable** primary key that
> is **not** an `AutoField`, the editing of inlines fails.
>
> This is because then the hidden field to "backlink" to the original item
> fails: there is no `<input type="hidden" id="id_child_set-0-id"
> name="child_set-0-id"> in the formsets, so no instances are attached to
> the forms of the formset. At best this would thus create new instances,
> at worst, it will in case of the UUID just fail to edit the inline
> objects and thus reject the entire form(set) and therefore reject the
> edit of the object in general.
>
> The steps to reproduce these are using models:
>
> {{{
> class Parent(models.Model):
>     name = models.CharField(max_length=128)
>
> class Child(models.Model):
>     id = models.UUIDField(primary_key=True, default=uuid.uuid4)
>     name = models.CharField(max_length=128)
> }}}
>
> and then work with an admin:
>
> {{{
> class ChildInline(admin.TabularInline):
>     model = Child
>     exclude = ("id",)  # important
>     extra = 1
>     verbose_name = "Subexample"
>     show_change_link = True
>
> @admin.register(Parent)
> class ParentAdmin(admin.ModelAdmin):
>     search_fields = ("name", )
>     inlines = [ChildInline,]
> }}}
>
> An easy workaround is to mark the `id` field of the `Child` as
> `editable=False`, which will resolve the issue. But it is not said that
> the UUID should *never* be editable, it is for example possible to
> exclude that for the inline, but then use it for another `ModelAdmin`,
> perhaps to duplicate to another UUID, or just use another primary key
> field altogether.
>
> The fix turned out to be quite minimal: just ensure that the primary key
> field is added, so in the `helpers.py`, for the `InlineAdminForm`, we
> use:
>
> {{{
>     def needs_explicit_pk_field(self):
>         return (
>             # Auto fields are editable, so check for auto or non-editable
> pk.
>             self.form._meta.model._meta.auto_field
>             or not self.form._meta.model._meta.pk.editable
>             or self.form._meta.model._meta.pk.name in
> (self.form._meta.exclude or ())
>             or
>             # Also search any parents for an auto field. (The pk info is
>             # propagated to child models so that does not need to be
> checked
>             # in parents.)
>             any(
>                 parent._meta.auto_field or not
> parent._meta.model._meta.pk.editable
>                 for parent in
> self.form._meta.model._meta.get_parent_list()
>             )
>         )
> }}}

New description:

 This issue was reported on [https://stackoverflow.com/a/78362210/67579
 StackOverflow]: if we have a model with an **editable** primary key that
 is **not** an `AutoField`, the editing of inlines fails.

 This is because then the hidden field to "backlink" to the original item
 fails: there is no `<input type="hidden" id="id_child_set-0-id"
 name="child_set-0-id"> in the formsets, so no instances are attached to
 the forms of the formset. At best this would thus create new instances, at
 worst, it will in case of the UUID just fail to edit the inline objects
 and thus reject the entire form(set) and therefore reject the edit of the
 object in general.

 The steps to reproduce these are using models:

 {{{
 class Parent(models.Model):
     name = models.CharField(max_length=128)

 class Child(models.Model):
     id = models.UUIDField(primary_key=True, default=uuid.uuid4)
     name = models.CharField(max_length=128)
     parent = models.ForeignKey(Parent, on_delete=models.CASCADE)
 }}}

 and then work with an admin:

 {{{
 class ChildInline(admin.TabularInline):
     model = Child
     exclude = ("id",)  # important
     extra = 1
     verbose_name = "Subexample"
     show_change_link = True

 @admin.register(Parent)
 class ParentAdmin(admin.ModelAdmin):
     search_fields = ("name", )
     inlines = [ChildInline,]
 }}}

 An easy workaround is to mark the `id` field of the `Child` as
 `editable=False`, which will resolve the issue. But it is not said that
 the UUID should *never* be editable, it is for example possible to exclude
 that for the inline, but then use it for another `ModelAdmin`, perhaps to
 duplicate to another UUID, or just use another primary key field
 altogether.

 The fix turned out to be quite minimal: just ensure that the primary key
 field is added, so in the `helpers.py`, for the `InlineAdminForm`, we use:

 {{{
     def needs_explicit_pk_field(self):
         return (
             # Auto fields are editable, so check for auto or non-editable
 pk.
             self.form._meta.model._meta.auto_field
             or not self.form._meta.model._meta.pk.editable
             or self.form._meta.model._meta.pk.name in
 (self.form._meta.exclude or ())
             or
             # Also search any parents for an auto field. (The pk info is
             # propagated to child models so that does not need to be
 checked
             # in parents.)
             any(
                 parent._meta.auto_field or not
 parent._meta.model._meta.pk.editable
                 for parent in
 self.form._meta.model._meta.get_parent_list()
             )
         )
 }}}

--
Comment:

 Thank you for the report Willem!
 Replicated on main and can see your patch resolves the issue (thank you
 for providing that). This just needs a test to be accepted 👍
-- 
Ticket URL: <https://code.djangoproject.com/ticket/35393#comment:1>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.

-- 
You received this message because you are subscribed to the Google Groups 
"Django updates" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to django-updates+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-updates/0107018f0a233460-5fcb129e-e30c-4c61-80b9-f50fd5f02ce0-000000%40eu-central-1.amazonses.com.

Reply via email to