[Django] #36251: BaseInlineFormSet modifies Meta.fields of passed form class if Meta.fields is type list

18 views
Skip to first unread message

Django

unread,
Mar 12, 2025, 11:41:07 AMMar 12
to django-...@googlegroups.com
#36251: BaseInlineFormSet modifies Meta.fields of passed form class if Meta.fields
is type list
--------------------------------+--------------------------------------
Reporter: ifeomi | Type: Bug
Status: new | Component: Forms
Version: 5.2 | Severity: Normal
Keywords: inline formset | Triage Stage: Unreviewed
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
--------------------------------+--------------------------------------
When you make an inline formset, `BaseInlineFormset` adds the foreign key
name to the list of fields. However, it only creates a copy of the
original form’s fields if the form defines its fields in a tuple;
otherwise, it modifies the original reference to fields. If you pass in a
form class that has defined `Meta.fields` as a list, `BaseInlineFormset`
changes the fields on that form class. This means that if that form is
used for other things besides the inline formset, it now has an extra
field that has been erroneously added.

Here is a minimally reproducible example. We have a `UserActionForm` that
defines `Meta.fields` as a list. Notice that after initializing an
instance of the `FormsetClass`, the fields on `UserActionForm` have been
modified to include `user` (the foreign key of the inline formset).
Expected behavior is that `UserActionForm.Meta.fields` is not modified by
instantiation of a formset, and instead that the formset modifies a copy
of the fields.

{{{#!python
## models.py ###
class User(AbstractUser):
email = models.EmailField(unique=True)

class UserAction(models.Model):
user = models.ForeignKey(User)
url = models.URLField(max_length=2083)

## forms.py ###
class UserActionForm(forms.ModelForm):
class Meta:
model = UserAction
fields = ["url"]

### Shell ###
from common.models import User, UserAction
from django import forms

FormsetClass = forms.inlineformset_factory(User, UserAction,
UserActionForm)
print(UserActionForm.Meta.fields)
# ['url']
FormsetClass()
print(UserActionForm.Meta.fields)
# ['url', 'user'] --> (should just be ['url'])
}}}

Here’s the line in `BaseInlineFormset`'s `__init__` that modifies the
form’s fields - in the event that the form’s fields are not a `tuple`, the
init appends directly to `fields` without making a copy.

{{{#!python
# django/forms/models.py:L1115
class BaseInlineFormSet(BaseModelFormSet):
def __init__(...):
...
if isinstance(self.form._meta.fields,
tuple):
self.form._meta.fields =
list(self.form._meta.fields)
self.form._meta.fields.append(self.fk.name)
}}}

The fix for this should be fairly straightforward: rather than only
copying `_meta.fields` if it’s a tuple, `BaseInlineFormset` should always
make a copy of `_meta.fields` regardless of the type of the iterable.
`BaseInlineFormset` already works with a copy in the case that the
original form’s fields are a tuple, so this change will maintain the
current behavior while preventing modifications to the original form’s
fields.

**Proposed Patch:**

{{{#!diff
diff --git a/django/forms/models.py b/django/forms/models.py
index d220e3c90f..4e8ef39c0d 100644
--- a/django/forms/models.py
+++ b/django/forms/models.py
@@ -1113,8 +1113,7 @@ class BaseInlineFormSet(BaseModelFormSet):
# Add the generated field to form._meta.fields if it's defined to
make
# sure validation isn't skipped on that field.
if self.form._meta.fields and self.fk.name not in
self.form._meta.fields:
- if isinstance(self.form._meta.fields, tuple):
- self.form._meta.fields = list(self.form._meta.fields)
+ self.form._meta.fields = list(self.form._meta.fields)
self.form._meta.fields.append(self.fk.name)

def initial_form_count(self):
}}}
--
Ticket URL: <https://code.djangoproject.com/ticket/36251>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.

Django

unread,
Mar 12, 2025, 11:58:23 AMMar 12
to django-...@googlegroups.com
#36251: BaseInlineFormSet modifies Meta.fields of passed form class if Meta.fields
is type list
--------------------------------+--------------------------------------
Reporter: ifeomi | Owner: (none)
Type: Bug | Status: new
Component: Forms | Version: 5.2
Severity: Normal | Resolution:
Keywords: inline formset | Triage Stage: Unreviewed
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
--------------------------------+--------------------------------------
Description changed by ifeomi:

Old description:
New description:
--
Ticket URL: <https://code.djangoproject.com/ticket/36251#comment:1>

Django

unread,
Mar 12, 2025, 12:20:31 PMMar 12
to django-...@googlegroups.com
#36251: BaseInlineFormSet modifies Meta.fields of passed form class if Meta.fields
is type list
--------------------------------+------------------------------------
Reporter: ifeomi | Owner: (none)
Type: Bug | Status: new
Component: Forms | Version: 5.2
Severity: Normal | Resolution:
Keywords: inline formset | Triage Stage: Accepted
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
--------------------------------+------------------------------------
Changes (by Clifford Gama):

* stage: Unreviewed => Accepted

--
Ticket URL: <https://code.djangoproject.com/ticket/36251#comment:2>

Django

unread,
Mar 12, 2025, 12:21:40 PMMar 12
to django-...@googlegroups.com
#36251: BaseInlineFormSet modifies Meta.fields of passed form class if Meta.fields
is type list
--------------------------------+--------------------------------------
Reporter: ifeomi | Owner: (none)
Type: Bug | Status: new
Component: Forms | Version: 5.2
Severity: Normal | Resolution:
Keywords: inline formset | Triage Stage: Unreviewed
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
--------------------------------+--------------------------------------
Changes (by Sarah Boyce):

* stage: Accepted => Unreviewed

Comment:

Thank you for the report!
Replicated
{{{#!diff
--- a/tests/model_formsets/tests.py
+++ b/tests/model_formsets/tests.py
@@ -1685,9 +1685,10 @@ class ModelFormsetTest(TestCase):

class Meta:
model = Book
- fields = ("title",)
+ fields = ["title"]

BookFormSet = inlineformset_factory(Author, Book, form=BookForm)
+ self.assertEqual(BookForm.Meta.fields, ["title"])
data = {
"book_set-TOTAL_FORMS": "3",
"book_set-INITIAL_FORMS": "0",
@@ -1698,6 +1699,7 @@ class ModelFormsetTest(TestCase):
}
author = Author.objects.create(name="test")
formset = BookFormSet(data, instance=author)
+ self.assertEqual(BookForm.Meta.fields, ["title"])
self.assertEqual(
formset.errors,
[{}, {"__all__": ["Please correct the duplicate values
below."]}, {}],
}}}
--
Ticket URL: <https://code.djangoproject.com/ticket/36251#comment:3>

Django

unread,
Mar 12, 2025, 12:22:58 PMMar 12
to django-...@googlegroups.com
#36251: BaseInlineFormSet modifies Meta.fields of passed form class if Meta.fields
is type list
--------------------------------+------------------------------------
Reporter: ifeomi | Owner: (none)
Type: Bug | Status: new
Component: Forms | Version: dev
Severity: Normal | Resolution:
Keywords: inline formset | Triage Stage: Accepted
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
--------------------------------+------------------------------------
Changes (by Sarah Boyce):

* stage: Unreviewed => Accepted
* version: 5.2 => dev

--
Ticket URL: <https://code.djangoproject.com/ticket/36251#comment:4>

Django

unread,
Mar 12, 2025, 12:33:12 PMMar 12
to django-...@googlegroups.com
#36251: BaseInlineFormSet modifies Meta.fields of passed form class if Meta.fields
is type list
--------------------------------+------------------------------------
Reporter: ifeomi | Owner: jengziyi
Type: Bug | Status: assigned
Component: Forms | Version: dev
Severity: Normal | Resolution:
Keywords: inline formset | Triage Stage: Accepted
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
--------------------------------+------------------------------------
Changes (by jengziyi):

* owner: (none) => jengziyi
* status: new => assigned

--
Ticket URL: <https://code.djangoproject.com/ticket/36251#comment:5>

Django

unread,
Mar 12, 2025, 1:07:12 PMMar 12
to django-...@googlegroups.com
#36251: BaseInlineFormSet modifies Meta.fields of passed form class if Meta.fields
is type list
--------------------------------+------------------------------------
Reporter: ifeomi | Owner: jengziyi
Type: Bug | Status: assigned
Component: Forms | Version: dev
Severity: Normal | Resolution:
Keywords: inline formset | Triage Stage: Accepted
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
--------------------------------+------------------------------------
Description changed by Benjamin Zagorsky:
user = models.ForeignKey(User, on_delete=models.PROTECT)
url = models.URLField(max_length=2083)

## forms.py ###
from django import forms
--
Ticket URL: <https://code.djangoproject.com/ticket/36251#comment:6>

Django

unread,
Mar 12, 2025, 1:08:09 PMMar 12
to django-...@googlegroups.com

Django

unread,
Mar 12, 2025, 1:09:44 PMMar 12
to django-...@googlegroups.com
#36251: BaseInlineFormSet modifies Meta.fields of passed form class if Meta.fields
is type list
--------------------------------+------------------------------------
Reporter: ifeomi | Owner: jengziyi
Type: Bug | Status: assigned
Component: Forms | Version: dev
Severity: Normal | Resolution:
Keywords: inline formset | Triage Stage: Accepted
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
--------------------------------+------------------------------------
Comment (by Benjamin Zagorsky):

I've verified the bug on Django 4.2 and Django 5.1.
--
Ticket URL: <https://code.djangoproject.com/ticket/36251#comment:8>

Django

unread,
Apr 18, 2025, 12:45:07 PMApr 18
to django-...@googlegroups.com
#36251: BaseInlineFormSet modifies Meta.fields of passed form class if Meta.fields
is type list
--------------------------------+------------------------------------
Reporter: ifeomi | Owner: (none)
Type: Bug | Status: new
Component: Forms | Version: dev
Severity: Normal | Resolution:
Keywords: inline formset | Triage Stage: Accepted
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
--------------------------------+------------------------------------
Changes (by jengziyi):

* owner: jengziyi => (none)
* status: assigned => new

--
Ticket URL: <https://code.djangoproject.com/ticket/36251#comment:9>

Django

unread,
Apr 18, 2025, 1:09:50 PMApr 18
to django-...@googlegroups.com
#36251: BaseInlineFormSet modifies Meta.fields of passed form class if Meta.fields
is type list
--------------------------------+------------------------------------
Reporter: ifeomi | Owner: jengziyi
Type: Bug | Status: assigned
Component: Forms | Version: dev
Severity: Normal | Resolution:
Keywords: inline formset | Triage Stage: Accepted
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
--------------------------------+------------------------------------
Changes (by jengziyi):

* owner: (none) => jengziyi
* status: new => assigned

--
Ticket URL: <https://code.djangoproject.com/ticket/36251#comment:10>

Django

unread,
Aug 18, 2025, 10:08:09 AMAug 18
to django-...@googlegroups.com
#36251: BaseInlineFormSet modifies Meta.fields of passed form class if Meta.fields
is type list
--------------------------------+--------------------------------------
Reporter: ifeomi | Owner: JaeHyuckSa
Type: Bug | Status: assigned
Component: Forms | Version: dev
Severity: Normal | Resolution:
Keywords: inline formset | Triage Stage: Accepted
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
--------------------------------+--------------------------------------
Changes (by JaeHyuckSa):

* owner: jengziyi => JaeHyuckSa

--
Ticket URL: <https://code.djangoproject.com/ticket/36251#comment:11>

Django

unread,
Aug 18, 2025, 10:43:49 AMAug 18
to django-...@googlegroups.com
#36251: BaseInlineFormSet modifies Meta.fields of passed form class if Meta.fields
is type list
--------------------------------+--------------------------------------
Reporter: ifeomi | Owner: JaeHyuckSa
Type: Bug | Status: assigned
Component: Forms | Version: dev
Severity: Normal | Resolution:
Keywords: inline formset | Triage Stage: Accepted
Has patch: 1 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
--------------------------------+--------------------------------------
Changes (by JaeHyuckSa):

* has_patch: 0 => 1

--
Ticket URL: <https://code.djangoproject.com/ticket/36251#comment:12>

Django

unread,
Aug 22, 2025, 10:05:46 AMAug 22
to django-...@googlegroups.com
#36251: BaseInlineFormSet modifies Meta.fields of passed form class if Meta.fields
is type list
-------------------------------------+-------------------------------------
Reporter: ifeomi | Owner:
| JaeHyuckSa
Type: Bug | Status: assigned
Component: Forms | Version: dev
Severity: Normal | Resolution:
Keywords: inline formset | Triage Stage: Ready for
| checkin
Has patch: 1 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Changes (by Sarah Boyce):

* stage: Accepted => Ready for checkin

--
Ticket URL: <https://code.djangoproject.com/ticket/36251#comment:13>

Django

unread,
Aug 25, 2025, 4:05:57 AMAug 25
to django-...@googlegroups.com
#36251: BaseInlineFormSet modifies Meta.fields of passed form class if Meta.fields
is type list
-------------------------------------+-------------------------------------
Reporter: ifeomi | Owner:
| JaeHyuckSa
Type: Bug | Status: closed
Component: Forms | Version: dev
Severity: Normal | Resolution: fixed
Keywords: inline formset | Triage Stage: Ready for
| checkin
Has patch: 1 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Changes (by Sarah Boyce <42296566+sarahboyce@…>):

* resolution: => fixed
* status: assigned => closed

Comment:

In [changeset:"3ba24c18e70dd242ae237fd955fb8be30f99bc4d" 3ba24c18]:
{{{#!CommitTicketReference repository=""
revision="3ba24c18e70dd242ae237fd955fb8be30f99bc4d"
Fixed #36251 -- Avoided mutating form Meta.fields in BaseInlineFormSet.

Signed-off-by: SaJH <wogur...@gmail.com>
}}}
--
Ticket URL: <https://code.djangoproject.com/ticket/36251#comment:14>
Reply all
Reply to author
Forward
0 new messages