Opened 13 years ago

Closed 6 years ago

Last modified 6 years ago

#16995 closed Bug (fixed)

ModelFormSet initial data from initial parameter uses "extra" forms

Reported by: p910221@… Owned by: Mark Gensler
Component: Documentation Version: dev
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

Description

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 by Carl Meyer, 13 years ago

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 by c v t, 13 years ago

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/formsets.py 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 by alex.isayko@…, 11 years ago

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 by Mark Gensler, 6 years ago

Owner: changed from nobody to Mark Gensler
Status: newassigned

I will work on adding this to the documentation.

comment:5 by Mark Gensler, 6 years ago

Has patch: set

Patch and pull request created at https://github.com/django/django/pull/10372

comment:6 by Carlton Gibson, 6 years ago

Triage Stage: AcceptedReady for checkin

comment:7 by Tim Graham <timograham@…>, 6 years ago

Resolution: fixed
Status: assignedclosed

In 28dac56a:

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

comment:8 by Tim Graham <timograham@…>, 6 years ago

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