On 03/19/2014 06:58 AM, Chris Wilson wrote: >> This requires overrides of a few methods on the parent form: __init__() >> to create the formsets, is_valid() to ensure the formsets are valid too, >> has_changed() to see if formsets have changed, and save() to save >> formsets too. > > There's a few more Form methods that I'm not sure whether I need to > override, especially if I want to be able to generalise this. I think > just _get_media and is_multipart should take sub-formsets into account?
Probably so, yeah - I haven't yet ever carried this through into a fully
reusable solution, just implemented it to meet my needs in a specific
case, which apparently never included form media or is-multipart. If
implementing a generic approach you'd want to do a thorough review of
the BaseForm API for places where encapsulated formsets might impact things.
> I think it would be nice for forms to be able to treat embedded formsets
> as a special (complex) type of 'field', so that as_table, as_p etc.
> would render the formsets as well, and for ModelForm to be able to
> generate these automatically for models with ForeignKeys that link back
> to the ModelForm's Model.
I don't use use the shortcut renderings (HTML generated in Python, ick),
so I've never looked into incorporating embedded formsets there.
Having an option to generate embedded formsets for linked models
automatically would be very cool.
I definitely think this idea should get proved out as a third-party app
before we consider it for core.
>>> 3. We couldn't change the "extra" of a formset without redefining the
>>> formset class. We wanted to do this because the required repeating
>>> sections (employment history etc.) must not have a blank form in them if
>>> they already have some valid forms, otherwise the blank forms prevent
>>> the form being submitted because HTML5 client-side validation fails. So
>>> we had to do all this:
>>
>> I don't understand this. The 'extra' attribute of a formset class can be
>> tweaked dynamically as a normal attribute (although this may not be
>> documented); it doesn't require recreating the entire formset class.
>
> I'm not sure that's possible, because inline_formset defines a new
> class. That class has a class attribute called 'extra' containing the
> value of 'extra', but changing it would change it for all instances of
> the class, so it's not thread-safe. And when the class is instantiated,
> the value is immediately used to create the initial empty forms, and not
> used after that, so changing it after instantiating the form has no
> effect. Thus, it seems that we have to subclass BaseFormSet anyway to
> get into the constructor and change the value of extra before the
> initial forms are created.
That's not quite accurate. The 'extra' attribute on a formset instance
isn't used immediately on instantiation, only lazily when the first
property on the formset is accessed that causes it to create all its
form instances. So there is a window immediately after instantiation
when it is safe and effective to set a different value for 'extra'
directly on the formset instance (not on the class, as you note). Or it
can be done in `__init__` of a BaseFormSet subclass (as you also note).
I think both of those approaches are easier/nicer than creating a whole
new formset class each time you need a different value for 'extra'.
To be clear, here's what I'm currently doing in a project:
class ConditionalExtraInlineFormset(BaseInlineFormSet):
"""Inline formset that adds one extra form if there are no initial
forms."""
def __init__(self, *a, **kw):
super(ConditionalExtraInlineFormset, self).__init__(*a, **kw)
self.extra = 0 if self.initial_form_count() else 1
But if you don't want to subclass for some reason, it would work just as
well to do the same thing from outside the instance, right after it is
instantiated.
>> I snipped your last three items, regarding saving invalid forms. I
>> think this is an unusual use case, and I'm not sure support for it
>> belongs in core. It would be interesting to experiment with something
>> to make filefields less painful when validation fails, but that can be
>> a third-party project first I think.
>
> It may be unusual, but I'm not the only one to want this. At least two
> people participated on this SO question:
> http://stackoverflow.com/questions/8993749/transform-an-unbound-form-to-a-bound-one
>
> From there I got my current approach, which is to use model_to_dict to
> create a fake 'data' to make the unbound form bound, so that I can
> validate it. However this is ugly, and it doesn't work with formsets
> because it doesn't account for the management form, which complains
> about having been tampered with because its data is missing. And there's
> no form_to_dict equivalent of model_to_dict, so I'd have to hack
> something up, all to pretend to Django that I've got an unbound form,
> just so I can validate its contents.
>
> Another approach to validating an unbound form appears to be to extract
> "value" from the model instance instead of cleaned_data, and this
> bypasses the management form issue, so I'm experimenting with that
> approach.
>
> It seems to me that there would be value in being officially able to
> validate unbound forms, and it might not be hard to implement.
Yes, I see the use case; not sure what the API would look like for it.
Carl
signature.asc
Description: OpenPGP digital signature
