Opened 3 years ago

Closed 3 years ago

Last modified 3 years ago

#33111 closed Bug (fixed)

Conditionally changing ModelAdmin inlines based on object's field breaks when changing object and new inlines should appear.

Reported by: joeli Owned by: Hasan Ramezani
Component: contrib.admin Version: 3.2
Severity: Normal Keywords:
Cc: Hasan Ramezani, WeizhongTu Triage Stage: Ready for checkin
Has patch: yes Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

Minimal example:

# models.py
class Parent(models.Model):
    show_inlines = models.BooleanField(default=False)

class Child(models.Model):
    parent = models.ForeignKey(Parent, on_delete=models.CASCADE)


# admin.py
class ChildInline(admin.StackedInline):
    model = Child

@admin.register(Parent)
class ParentAdmin(admin.ModelAdmin):
    def get_inlines(self, request, obj):
        if obj is not None and obj.show_inlines:
            return [ChildInline]
        return []
  • Create Parent objects in either initial state and it works as you'd expect, where ChildInline is rendered when show_inlines is True.
  • When show_inlines is True, you can also set it to False from the admin site, and the ChildInline disappears as expected.
  • But when show_inlines is False, you cannot re-enable it. Saving the object fails due to a validation error in the new ChildInline that didn't exist before saving:
(Hidden field TOTAL_FORMS) This field is required.
(Hidden field INITIAL_FORMS) This field is required.
ManagementForm data is missing or has been tampered with. Missing fields: child_set-TOTAL_FORMS, child_set-INITIAL_FORMS. You may need to file a bug report if the issue persists.

Change History (6)

comment:1 by Mariusz Felisiak, 3 years ago

Cc: Hasan Ramezani WeizhongTu added
Summary: Conditionally changing ModelAdmin inlines based on object's field breaks saving object in admin site when new inlines should appearConditionally changing ModelAdmin inlines based on object's field breaks when changing object and new inlines should appear.
Triage Stage: UnreviewedAccepted

Thanks for the report. I was able to fix this issue by passing an old instance to the get_inlines() 🤔:

diff --git a/django/contrib/admin/options.py b/django/contrib/admin/options.py
index 1e5fab917e..7b2b893c10 100644
--- a/django/contrib/admin/options.py
+++ b/django/contrib/admin/options.py
@@ -1585,12 +1585,16 @@ class ModelAdmin(BaseModelAdmin):
         )
         if request.method == 'POST':
             form = ModelForm(request.POST, request.FILES, instance=obj)
+            formsets, inline_instances = self._create_formsets(
+                request,
+                form.instance if add else obj,
+                change=not add,
+            )
             form_validated = form.is_valid()
             if form_validated:
                 new_object = self.save_form(request, form, change=not add)
             else:
                 new_object = form.instance
-            formsets, inline_instances = self._create_formsets(request, new_object, change=not add)
             if all_valid(formsets) and form_validated:
                 self.save_model(request, new_object, form, not add)
                 self.save_related(request, form, formsets, not add)

comment:2 by Hasan Ramezani, 3 years ago

Owner: changed from nobody to Hasan Ramezani
Status: newassigned

comment:3 by Hasan Ramezani, 3 years ago

Has patch: set

comment:4 by Mariusz Felisiak, 3 years ago

Triage Stage: AcceptedReady for checkin

comment:5 by Mariusz Felisiak <felisiak.mariusz@…>, 3 years ago

Resolution: fixed
Status: assignedclosed

In 2f0f30f:

Fixed #33111 -- Fixed passing object to ModelAdmin.get_inlines() when editing in admin change view.

ModelAdmin.get_inlines() should get an unmutated object when creating
formsets during POST request.

comment:6 by Mariusz Felisiak <felisiak.mariusz@…>, 3 years ago

In 668b990b:

[4.0.x] Fixed #33111 -- Fixed passing object to ModelAdmin.get_inlines() when editing in admin change view.

ModelAdmin.get_inlines() should get an unmutated object when creating
formsets during POST request.

Backport of 2f0f30f973363a59c20f204f9351724fb2ce7327 from main

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