Opened 5 years ago

Closed 5 years ago

Last modified 5 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, 5 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 appear → Conditionally changing ModelAdmin inlines based on object's field breaks when changing object and new inlines should appear.
Triage Stage: Unreviewed → Accepted

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, 5 years ago

Owner: changed from nobody to Hasan Ramezani
Status: new → assigned

comment:3 by Hasan Ramezani, 5 years ago

Has patch: set

comment:4 by Mariusz Felisiak, 5 years ago

Triage Stage: Accepted → Ready for checkin

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

Resolution: → fixed
Status: assigned → closed

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@…>, 5 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