Opened 7 years ago

Closed 4 months ago

Last modified 4 months ago

#16995 closed Bug (fixed)

ModelFormSet initial data from initial parameter uses "extra" forms

Reported by: p910221@… Owned by: S. Dole Melipone
Component: Documentation Version: master
Severity: Normal Keywords:
Cc: Triage Stage: Ready for checkin
Has patch: yes Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no


It seems that when you feed initial data into a ModelFromSet using the initial parameter, it uses the "extra" forms that you define with the extra parameter in the modelformset_factory. For example, if you feed a list of two dictionaries (with the model data) into the initial parameter of a FormSet, but the formset_factory that the FormSet was created from has an extra parameter of 1 (or None), then only the model data from the first dictionary in the list will show up in the FormSet.

Since "initial" data doen't seem like they should be in the "extra" forms, I think this is a bug?

Change History (8)

comment:1 Changed 7 years ago by Carl Meyer

Component: FormsDocumentation
Triage Stage: UnreviewedAccepted
Version: 1.3SVN

I believe this is working as designed. I do, however, think the documentation is lacking in clarity and there's an inconsistency with how initial works in a regular FormSet.

With a FormSet, extra forms are those beyond the ones created by the data in initial, as documented here.

With a ModelFormSet, the data in the non-extra forms is determined by the existing model instances. It's sometimes useful to be able to pre-populate some of the extra forms with some initial data, and that's what the initial parameter is used for. If initial pre-populated the non-extra forms, how would the conflict with existing model instance data be resolved?

If this were being designed from scratch, you could perhaps make a case that a ModelFormSet should consist of first the forms representing existing instances, then forms representing the given initial data, then empty extra forms (i.e. initial would add more forms, not prepopulate the extra forms). This would perhaps be more consistent with FormSet, but I'm not sure it's actually better for real usage.

In any case, if it's an improvement at all, it's not enough of one to justify making a backwards-incompatible change. So I'm converting this into a documentation bug; the ModelFormSet docs should have an explanation of how initial and extra interact differently in a ModelFormSet than in a FormSet.

comment:2 Changed 7 years ago by supervacuo

I was about to report this as a separate bug (after asking in #django-dev); I see this is as a feature proposal along the lines of "Set extra to the length of initial in ModelFormSet if it is not set manually".

I'm using modelformset_factory to create a model formset class, then passing initial in the constructor when instantiating it. Line 91 of forms/ uses the value of extra to work out how many extra forms should be drawn, but I think this information could already be implied by the length of the initial dict, at least in the case where extra is not specified (for backwards compatibility), and having to specify both seems a little like Repeating Yourself.

The code I'm using which demonstrates the problem is here.

I can certainly try writing a patch (it shouldn't be a big change, and I imagine the tests would be pretty straightforward) if this seems like a good idea. If not, I agree that the "Making forms from models" docs should explain that extra needs to be set, even with initial, using something like

from django.forms.models import modelformset_factory

foos = some_function_that_returns_a_list_of_foo()

FooFormSet = modelformset_factory(Foo, extra=len(foos))

foo_formset = FooFormSet(initial=[f.__dict__ for f in foos])

comment:3 Changed 5 years ago by alex.isayko@…

The quick workaround on this is to use lambda function:

from django.forms.models import modelformset_factory

foos = some_function_that_returns_a_list_of_foo()

FooFormSet = lambda *args, **kwargs: modelformset_factory(Foo, extra=kwargs.pop('extra', 0))(*args, **kwargs)

foo_formset = FooFormSet(initial=[f.__dict__ for f in foos], extra=len(foos))

comment:4 Changed 5 months ago by S. Dole Melipone

Owner: changed from nobody to S. Dole Melipone
Status: newassigned

I will work on adding this to the documentation.

comment:5 Changed 5 months ago by S. Dole Melipone

Has patch: set

Patch and pull request created at

comment:6 Changed 4 months ago by Carlton Gibson

Triage Stage: AcceptedReady for checkin

comment:7 Changed 4 months ago by Tim Graham <timograham@…>

Resolution: fixed
Status: assignedclosed

In 28dac56a:

Fixed #16995 -- Clarified interaction of initial and extra with model formsets.

comment:8 Changed 4 months ago by Tim Graham <timograham@…>

In 24b3aa02:

[2.1.x] Fixed #16995 -- Clarified interaction of initial and extra with model formsets.

Backport of 28dac56aed1c8c9923b52a1ac3606996f9820b30 from master

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