[Django] #21596: Add method to formset to add a form

7 views
Skip to first unread message

Django

unread,
Dec 11, 2013, 9:31:58 PM12/11/13
to django-...@googlegroups.com
#21596: Add method to formset to add a form
-----------------------------+--------------------
Reporter: nickname123 | Owner: nobody
Type: New feature | Status: new
Component: Forms | Version: 1.6
Severity: Normal | Keywords:
Triage Stage: Unreviewed | Has patch: 0
Easy pickings: 0 | UI/UX: 0
-----------------------------+--------------------
It is very complicated to add a form to a formset with the formset api.

It should be simple to do without javascript.

It would be nice if there was an add_form method on the formset that added
a empty form to formset.forms.

--
Ticket URL: <https://code.djangoproject.com/ticket/21596>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.

Django

unread,
Jan 2, 2014, 8:33:47 PM1/2/14
to django-...@googlegroups.com
#21596: Add method to formset to add a form
-----------------------------+--------------------------------------

Reporter: nickname123 | Owner: nobody
Type: New feature | Status: new
Component: Forms | Version: 1.6
Severity: Normal | Resolution:
Keywords: | Triage Stage: Unreviewed
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0

Easy pickings: 0 | UI/UX: 0
-----------------------------+--------------------------------------
Changes (by timo):

* needs_better_patch: => 0
* needs_tests: => 0
* needs_docs: => 0


Comment:

I'm not sure I understand the problem or proposal. You mention JavaScript
which makes me think you're adding a form on the client side, but then you
propose an `add_form` method which I assume is on the server?

--
Ticket URL: <https://code.djangoproject.com/ticket/21596#comment:1>

Django

unread,
Jan 2, 2014, 10:05:43 PM1/2/14
to django-...@googlegroups.com
#21596: Add method to formset to add a form
-----------------------------+--------------------------------------

Reporter: nickname123 | Owner: nobody
Type: New feature | Status: new
Component: Forms | Version: 1.6
Severity: Normal | Resolution:
Keywords: | Triage Stage: Unreviewed

Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0

Easy pickings: 0 | UI/UX: 0
-----------------------------+--------------------------------------

Comment (by nickname123):

I will elaborate. Sorry for the confusing description.

Right now, there is no easy way to add a form to a formset that I could
find documented. The formset api doesn't have anything to assist with
this internally that I could find either.

My use case is similar to the django admin where a user can click "add
another". The formset documentation suggests that this should be done
client side by javascript:
https://docs.djangoproject.com/en/dev/topics/forms/formsets/#empty-form

I think this should be possible to implement server side.

This is a simple mixin that demonstrates my intentions:

{{{
class AddableFormSet(BaseModelFormSet):
can_add_form = True

def add_form(self, **kwargs):
if self.is_valid():
# add the form
tfc = self.total_form_count()
form = self._construct_form(tfc, **kwargs)
form.is_bound = False
self.forms.append(form)

# make data mutable
self.data = self.data.copy()

# increase hidden form counts
total_count_name = '%s-%s' % (self.management_form.prefix,
TOTAL_FORM_COUNT)
self.data[total_count_name] =
self.management_form.cleaned_data[TOTAL_FORM_COUNT] + 1

@property
def add_form_key(self):
return self.add_prefix(ADD_FORM_KEY)
}}}


Now, it is easy to hook up in the view:


{{{
class CustomWizard(NamedUrlSessionWizardView):
def get_form(self, step=None, data=None, files=None):
# could be a form or formset
formish = super(CustomWizard, self).get_form(step=step,
data=data, files=files)

# support adding forms to formsets if there is post data
if data:
if isinstance(formish, BaseModelFormSet) and
formish.can_add_form:
if formset.add_form_key in data:
formset.add_form()
}}}

Now, in the template you just need something like this:

{{{
{{ formset.management_form }}
{% for form in formset %}
{{ form }}
{% endfor %}

{% if formset.can_add_form %}
<hr />
<input name="{{ formset.add_form_key }}" type="submit" value="Add
another" />
<hr />
{% endif %}
}}}

This really makes the formset more useful to me. It is simple to
implement and now doesn't require javascript to implement the "add
another" button.

It took me a pretty long time to figure out the internals to implement
this so I doubt someone new to the framework would be able to. This seems
like a logical addition to the formset api. (aside: I chose to require
is_valid() in the add_form() method because it would be frustrating for
the user to create 15 forms to find out they are all invalid. The way I
have implemented it supports multiple formsets on the same page. Which is
important for my use case that mixes individual forms and formsets in a
"form container")

It is fairly straightforward and I think that any formset used for data
entry could make use of this. So I think it would be useful to almost
anyone using a formset.

--
Ticket URL: <https://code.djangoproject.com/ticket/21596#comment:2>

Django

unread,
Mar 28, 2014, 8:28:04 PM3/28/14
to django-...@googlegroups.com
#21596: Add method to formset to add a form
-----------------------------+--------------------------------------
Reporter: nickname123 | Owner: nobody
Type: New feature | Status: closed
Component: Forms | Version: 1.6
Severity: Normal | Resolution: wontfix
Keywords: | Triage Stage: Unreviewed

Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0

Easy pickings: 0 | UI/UX: 0
-----------------------------+--------------------------------------
Changes (by timo):

* status: new => closed
* resolution: => wontfix


Comment:

This seems a bit over-engineered to me. Couldn't you accomplish something
similar by passing `extra=1` to the `formset_factory` and then simply
reload the page on submit to get another form for your formset?

--
Ticket URL: <https://code.djangoproject.com/ticket/21596#comment:3>

Django

unread,
Mar 28, 2014, 8:39:30 PM3/28/14
to django-...@googlegroups.com
#21596: Add method to formset to add a form
-----------------------------+--------------------------------------
Reporter: nickname123 | Owner: nobody
Type: New feature | Status: closed
Component: Forms | Version: 1.6
Severity: Normal | Resolution: wontfix
Keywords: | Triage Stage: Unreviewed

Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0

Easy pickings: 0 | UI/UX: 0
-----------------------------+--------------------------------------

Comment (by nickname123):

Maybe my solution isn't what django should go with, but you really don't
see the utility in a method that adds another form to an existing formset?

I wasn't trying to propose my hack as the way to do it. I just posted it
to demonstrate the concept. I pieced it together quickly from searching
documentation and google without finding any clear way to add a form to an
existing formset. And there currently isn't a straight forward way to
achieve this

--
Ticket URL: <https://code.djangoproject.com/ticket/21596#comment:4>

Django

unread,
Mar 28, 2014, 8:55:22 PM3/28/14
to django-...@googlegroups.com
#21596: Add method to formset to add a form
-----------------------------+--------------------------------------
Reporter: nickname123 | Owner: nobody
Type: New feature | Status: closed
Component: Forms | Version: 1.6
Severity: Normal | Resolution: wontfix
Keywords: | Triage Stage: Unreviewed

Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0

Easy pickings: 0 | UI/UX: 0
-----------------------------+--------------------------------------

Comment (by timo):

Why not the solution I proposed?

--
Ticket URL: <https://code.djangoproject.com/ticket/21596#comment:5>

Django

unread,
Mar 29, 2014, 7:02:33 PM3/29/14
to django-...@googlegroups.com
#21596: Add method to formset to add a form
-----------------------------+--------------------------------------
Reporter: nickname123 | Owner: nobody
Type: New feature | Status: closed
Component: Forms | Version: 1.6
Severity: Normal | Resolution: wontfix
Keywords: | Triage Stage: Unreviewed

Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0

Easy pickings: 0 | UI/UX: 0
-----------------------------+--------------------------------------

Comment (by nickname123):

Replying to [comment:5 timo]:


> Why not the solution I proposed?

It does not seem intuitive to me, but it seems like your proposed solution
would work similarly in some situations but not all.

My major problem with it is that it requires all formsets to be created
dynamically when needed and you can only specify to include an extra form
initially when the factory function is called. This seems like a very
complicated path to take just to avoid adding a small change to the
formset class. Maybe this is a bad comparison, but it seems kind of like
having a list [] that you must specify how many elements are required in
advance vs having an append method.


{{{
def add_form(self, **kwargs):
self.forms.append(self._construct_form(self.total_form_count(),
**kwargs))
self.forms[-1].is_bound = False
self.data = self.data.copy()
self.data['%s-%s' % (self.management_form.prefix,
TOTAL_FORM_COUNT)] = self.management_form.cleaned_data[TOTAL_FORM_COUNT] +
1
}}}

Note that TOTAL_FORM_COUNT is a key string imported from
django.forms.formsets, not something I calculate elsewhere.

My change to the formset class only needs to add 5 lines if the
comments/verboseness isn't important. And it makes adding forms to an
existing formset possible and much more flexible. The is_valid() check
probably shouldn't be added to the main formset class. That was just for
a particular use case I wrote the mixin for.

I prefer to call the formset_factory method once and use it like a form
class definition that I import elsewhere for readability. My preference
probably isn't important to Django, but I figured I would mention it.

I.e.

{{{
...
OptionalPhoneNumberModelFormset = modelformset_factory(PhoneNumber,
form=PhoneNumberModelForm, formset=EntryOnlyAddableFormSet)
FirstRequiredPhoneNumberModelFormset = modelformset_factory(PhoneNumber,
form=PhoneNumberModelForm, formset=FirstRequiredEntryOnlyAddableFormSet)
...
}}}

For another case where the add_form method would be preferable see:
https://code.djangoproject.com/ticket/18830

I am using a version of russelm's formcontainer that I have been working
on and it is much easier to use the "add_form" method than recreating the
formcontainer and its children when an additional form needs to be added
to one of the child formsets.

--
Ticket URL: <https://code.djangoproject.com/ticket/21596#comment:6>

Reply all
Reply to author
Forward
0 new messages