Conditionally changing ModelAdmin inlines based on object's field breaks when changing object and new inlines should appear.
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)
| 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
|
| Owner: |
changed from nobody to Hasan Ramezani
|
| Status: |
new → assigned
|
| Triage Stage: |
Accepted → Ready for checkin
|
| Resolution: |
→ fixed
|
| Status: |
assigned → closed
|
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)