--
Ticket URL: <https://code.djangoproject.com/ticket/16995>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.
* needs_better_patch: => 0
* component: Forms => Documentation
* needs_tests: => 0
* version: 1.3 => SVN
* needs_docs: => 0
* stage: Unreviewed => Accepted
Comment:
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
[https://docs.djangoproject.com/en/dev/topics/forms/formsets/#using-
initial-data-with-a-formset 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`
[https://docs.djangoproject.com/en/dev/topics/forms/modelforms/#model-
formsets docs] should have an explanation of how `initial` and `extra`
interact differently in a `ModelFormSet` than in a `FormSet`.
--
Ticket URL: <https://code.djangoproject.com/ticket/16995#comment:1>
Comment (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/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
[http://dpaste.org/QPWmn/ 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])
}}}
--
Ticket URL: <https://code.djangoproject.com/ticket/16995#comment:2>
Comment (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))
}}}
--
Ticket URL: <https://code.djangoproject.com/ticket/16995#comment:3>
* owner: nobody => S. Dole Melipone
* status: new => assigned
Comment:
I will work on adding this to the documentation.
--
Ticket URL: <https://code.djangoproject.com/ticket/16995#comment:4>
* has_patch: 0 => 1
Comment:
Patch and pull request created at
https://github.com/django/django/pull/10372
--
Ticket URL: <https://code.djangoproject.com/ticket/16995#comment:5>
* stage: Accepted => Ready for checkin
--
Ticket URL: <https://code.djangoproject.com/ticket/16995#comment:6>
* status: assigned => closed
* resolution: => fixed
Comment:
In [changeset:"28dac56aed1c8c9923b52a1ac3606996f9820b30" 28dac56a]:
{{{
#!CommitTicketReference repository=""
revision="28dac56aed1c8c9923b52a1ac3606996f9820b30"
Fixed #16995 -- Clarified interaction of initial and extra with model
formsets.
}}}
--
Ticket URL: <https://code.djangoproject.com/ticket/16995#comment:7>
Comment (by Tim Graham <timograham@…>):
In [changeset:"24b3aa0275717029f658906c6fbc2e8fec28949e" 24b3aa02]:
{{{
#!CommitTicketReference repository=""
revision="24b3aa0275717029f658906c6fbc2e8fec28949e"
[2.1.x] Fixed #16995 -- Clarified interaction of initial and extra with
model formsets.
Backport of 28dac56aed1c8c9923b52a1ac3606996f9820b30 from master
}}}
--
Ticket URL: <https://code.djangoproject.com/ticket/16995#comment:8>