I am really finding the django formsets confusing.
I am especially having problems with the following concepts which I don't really understand:
The formset is smart enough to ignore extra forms that were not changed.
Talking about code trying to be too smart. What is this supposed to mean exactly ? Why would I even want that ?
Then, trying to understand the previous concept, I see people
making forms in the formsets required.
This is another concept I can't get the hang of. What is a required form in a formset and why do I have to make a form required ? Again something not documented.
Then coming to my actual problem, which other people seem to have had, but they can't really explain why they've fixed it the way they've fixed it.
Why in the following example, the formset is valid, while an individual form with the same input will be invalid ?
import django
class MyForm(django.forms.Form):
start = django.forms.DateField()
end = django.forms.DateField()
data = {
'form-TOTAL_FORMS': '1',
'form-MAX_NUM_FORMS': '',
'form-INITIAL_FORMS': '0',
'form-0-start': '',
'form-0-end': '',
}
MyFormSet = formset_factory(MyForm)
formset = MyFormSet(data)
#fee_forms[0].empty_permitted = False
print formset.is_valid()
# --- returns True ---
print formset.errors
f = MyForm({'start': '', 'end': ''})
print f.is_valid()
# --- returns False ---
print f.errors
Setting empty_permitted to False seems to give the expected results for me (which is for the formset to be invalid due to missing 'start' and 'end'). This is another undocumented feature ...
Would anybody spare some time to explain ?
Thank you
The formset is smart enough to ignore extra forms that were not changed.
Talking about code trying to be too smart. What is this supposed to mean exactly? Why would I even want that?
It seems to mean — as you worked out — that "extra" forms created by the formset (with extra=N
in your example) have empty_permitted
set to True
. Have a glance at django/forms/formsets.py
to see this happening.
formset[0].empty_permitted
means that if formset[0].has_changed() == False
, no further processing/validation is done. Again, you can see this in action in forms/forms.py
.
To prevent this, a blog post suggests defining a custom formset to use in modelformset_factory
(or inlineformset_factory
) which sets empty_permitted = False
:
class MyModelFormSet(BaseModelFormSet):
def __init__(self, *args, **kwargs):
super(MyModelFormSet, self).__init__(*args, **kwargs)
for form in self.forms:
form.empty_permitted = False
I haven't tested this, but it looks legit.
As to why anyone might want this, it makes using
django-dynamic-formset
much simpler — you can send data for form-0
and form-2
and, assuming form-1
was an extra form (i.e. not linked to model data), Django won't complain. If empty_permitted
were False
by default, you'd have to worry about skipping the blank form in your own code, or reindexing things in Javascript.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With