On 20 Sep 2013, at 9:55, Russell Keith-Magee wrote:

On Fri, Sep 20, 2013 at 2:41 PM, Marc Tamlyn <[email protected]> wrote:

This is partly because there's no obvious correct implementation of them ;)

Yes, I think these views should exist. But they go with the same body of work as handling multiple forms, inline formsets etc. At present I have no yet found the time to think about this problem, as a whole and come up with
a single consistent solution.

I'm not sure FormSetView *should* exist. I'd be a lot more inclined to look at something like FormContainer -- a form-like container that can contain multiple forms and formsets. There's a sample implementation on #18830 in the context of extending FormWizard to support combinations of form and
formset.

https://code.djangoproject.com/ticket/18830

If you had a form container, you wouldn't need a new generic view -- you
can just use FormView, and use the FormContainer as the form.

(I'd also like to rethink generics in general, but that's a whole other
conversation… and one that I need to internally progress from vauge
rambling to slightly coherent proposal… and then find time to work on :-)

Yours,
Russ Magee %-)


+1 on having a way to arbitrarily compose forms and formsets on django's core.

Having built ways to nest forms and formsets within forms, I'll have to agree with Andrew that having something like `forms.SubForm(MyForm)` or `forms.SubFormSet(MyFormSet)` would be a better api than having a generic view or an abstraction between views and forms (that's what I assume you mean with FormContainer).

Composing multiple forms or formsets by adding `nested_form = forms.SubForm(MyForm)` in the form definition seems like a natural extension of the Form api and presents a lot of benefits:

1. It is apparent to the api user that the parent form would maintain the same external api. `my_form.is_valid`, `my_form.save` on a ModelForm, `{{ my_form }}` in the template, etc. would still work and take into account the nested forms. Having a different object act as a form/formset container would mean that either the users would have to learn another api, or that the container would have to mirror the form api (in which case, it might as well be a form). This also means that (depending on the use case) you could add sub forms/formsets on your form and have your view and template code remain unchanged.

2. It also maintains the same internal api, since adding a form/formset and customising its validation would be the same as adding any other field. This also allows you to mix regular fields with sub forms/formsets.

3. It allows arbitrary composition of form/formsets. Since the container object would be a regular form, it could be used wherever a form could be used. This means that you could use a form containing a sub form/formset with formsets, form wizards, generic form views etc. without having to do any additional work. This would also trivially solve the formsets-within-formsets problem (my original use case) that a lot of people seem to be having trouble with (google "django nested formsets").

4. It opens the road for more intelligent ModelForms. This wouldn't be done automatically for backwards compatibility reasons, but having a well defined way to nest sub forms/formsets within forms could be extended to automatically create them for OneToOneFields and ManyToManyFields.

Having said all that, there are a few factors that would make the implementation and usage a bit tricky. In no particular order:

1. The Fields/Widget api is not well suited for `SubForm/Formset` fields. I could elaborate more on this, but the crux of the problem is that fields and widgets are used internally in a stateless manner, while forms and formsets need to keep track of a lot of state. That said, I think it could be done.

2. It requires modifications of `django.forms.Form` (and potentially of `django.forms.BoundField`, depending on the template api it would expose). This means that this couldn't be done in a third-party app as a proof of concept. My solution has been to create a custom Form subclass and inherit from it whenever I need the sub form/formset functionality, but this is incompatible with other apps that require you to inherit from their custom Form subclass (eg. floppyforms).

3. There are issues with saving sub forms/formsets. Saving an outer form with `commit=False` cannot really return an object that would save everything when its `save` method is called, due to the same orm limitations that has necessitated the `save_m2m` method. The optimal solution would be to solve this in the orm layer, but I have no idea if this is feasible. My solution is to create additional `save_forms` and `save_formsets` methods on the form instance, plus a fourth `save_related` method that just calls the other 3. The result is that the form's external api remains similar, but you have to call `save_related` instead of `save_m2m`.

4. There are lots of moving parts and edge cases in the interactions between the parent form, the sub form/formset field and the actual nested form/formset. I'm sure there are a lot of these but here's a sample of what i've run into:

- should the parent form's prefix be prepended to the child form's prefix? - should the form field's name be prepended to the child form's prefix (to support having the same form nested twice inside a parent form)? - should `auto_id`, `error_class` and `label_suffix` be propagated to child forms? - should `required=False` on the SubForm field be translated to `empty_permitted=True` on the sub form?

I believe that all of the above have sensible answers but they will certainly require some thought.

5. This isn't exactly django's problem and it is specific to the formsets-within-formsets scenario, but things get messy on the javascript side if you want to be able to add/remove forms client side with nested formsets.






--
You received this message because you are subscribed to the Google Groups "Django 
developers" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To post to this group, send email to [email protected].
Visit this group at http://groups.google.com/group/django-developers.
For more options, visit https://groups.google.com/groups/opt_out.

Reply via email to