Code

Opened 9 months ago

Last modified 6 months ago

#21332 new Cleanup/optimization

BaseInlineFormSet.add_fields adds multiple InlineForeignKeyFields

Reported by: Raumkraut Owned by: nobody
Component: Forms Version: 1.6-beta-1
Severity: Normal Keywords:
Cc: Triage Stage: Accepted
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

When a formset object has its forms iterated over, the _construct_form(...) method calls self.add_fields(...) against each form in the set.

However, for a BaseInlineFormSet, the add_fields implementation creates an InlineForeignKeyField, and appends it to form._meta.fields. It does this for each form, and since form._meta is a class attribute, it ends up with multiple duplicate InlineForeignKeyField instances.

This doesn't appear to be normally evidenced in any usual output, since all the generated FK fields have the same name, so overwrite each other in name-keyed dictionaries of the fields.

Tested on the latest stable/1.6.x branch

Attachments (0)

Change History (5)

comment:1 in reply to: ↑ description Changed 9 months ago by Raumkraut

  • Needs documentation unset
  • Needs tests unset
  • Patch needs improvement unset

Also note that, unless I'm mistaken, the InlineForeignKeyField instances will continue to be appended to _meta.fields for as long as the server is running, and formsets are being iterated over. Which would make this something of a memory leak.

comment:2 Changed 8 months ago by timo

Do you have any ideas to address this? Could you provide a sample project that demonstrates the problem?

comment:3 Changed 8 months ago by Raumkraut

Okay, so I was wrong about what was getting appended to _meta.fields: It's not InlineForeignKeyField instances which get added, it's only the name of the foreign-key field.

I've cooked up a basic project to demonstrate the issue:
https://github.com/Raumkraut/test_formsets
It should be enough to clone, syncdb and runserver. The root page demonstrates the issue in question.

Since it is just the name of the field which gets appended repeatedly, unless I've missed something, it seems a fix could be fairly simple:

diff --git a/django/forms/models.py b/django/forms/models.py
index 71da433..20a3d54 100644
--- a/django/forms/models.py
+++ b/django/forms/models.py
@@ -893,7 +893,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 form._meta.fields:
+        if form._meta.fields and self.fk.name not in form._meta.fields:
             if isinstance(form._meta.fields, tuple):
                 form._meta.fields = list(form._meta.fields)
             form._meta.fields.append(self.fk.name)

comment:4 Changed 8 months ago by akaariai

  • Triage Stage changed from Unreviewed to Accepted

I can reproduce, on 1.5.x, 1.6.x and master. The project doesn't seem to be doing anything special.

comment:5 Changed 6 months ago by carljm

  • Type changed from Uncategorized to Cleanup/optimization

Add Comment

Modify Ticket

Change Properties
<Author field>
Action
as new
The owner will be changed from nobody to anonymous. Next status will be 'assigned'
as The resolution will be set. Next status will be 'closed'
Author


E-mail address and user name can be saved in the Preferences.

 
Note: See TracTickets for help on using tickets.