Django form control not working because "form-TOTAL_FORMS" and "form-INITIAL_FORMS" are not filled out correctly
Information:
I would like to create nested forms as best described in the example provided in:
http://yergler.net/blog/2009/09/27/nested-formsets-with-django/
The tutorial on this page seems to be pretty good && it tries to accomplish the exact problem I'm running into.
There seems to be a problem with this implementation in the views.py file when there is no POST request data (Ie we are executing the initial aggregate from the database).
The code can be seen at the above URL (I can post some of the code if necessary, but I'm afraid it will take away the information provided here).
Here is the Views.py code that fails (in bold):
block = get_object_or_404(models.Block, id=block_id)
if request.method == 'POST':
formset = forms.BuildingFormset(request.POST, instance=block)
if formset.is_valid():
rooms = formset.save_all()
return redirect('block_view', block_id=block.id)
else:
formset = forms.BuildingFormset(instance=block) #This is the line that is throwing the ValidationError
The error message I'm getting is:
ValidationError at "urlName":
[u'ManagementForm data is missing or has been tampered with']
I dug deeper and it seems that this crash is happening at site-packages / django / forms / formsets.py line
Validation is_valid()
does not work because some of the data required for the control form (form-TOTAL_FORMS, form-INITIAL_FORMS, and form-MAX_NUM_FORMS) is invalid. Here is the actual output of self.errors below:
{u'TOTAL_FORMS': [u'This field is required.'], u'INITIAL_FORMS': [u'This field is required.']}
code:
edit_building.html:
{{buildings.management_form}}
{% for building in buildings.%}
{{ building }} {% if building.nested %} {% for formset in building.nested %} {{ formset.as_table }} {% endfor %} {% endif %}
{% endfor%}
views.py:
def should_delete(self, form):
"""Convenience method for determining if the form’s object will
be deleted; cribbed from BaseModelFormSet.save_existing_objects."""
if self.can_delete:
raw_delete_value = form._raw_value(DELETION_FIELD_NAME)
should_delete = form.fields[DELETION_FIELD_NAME].clean(raw_delete_value)
return should_delete
return False
def save_all(self, commit=True):
"""Save all formsets and along with their nested formsets."""
# Save without committing (so self.saved_forms is populated)
# — We need self.saved_forms so we can go back and access
# the nested formsets
objects = self.save(commit=False)
# Save each instance if commit=True
if commit:
for o in objects:
o.save()
# save many to many fields if needed
if not commit:
self.save_m2m()
# save the nested formsets
for form in set(self.initial_forms + self.saved_forms):
if self.should_delete(form): continue
for nested in form.nested:
nested.save(commit=commit)
forms.py:
def should_delete(self, form):
"""Convenience method for determining if the form’s object will
be deleted; cribbed from BaseModelFormSet.save_existing_objects."""
if self.can_delete:
raw_delete_value = form._raw_value(DELETION_FIELD_NAME)
should_delete = form.fields[DELETION_FIELD_NAME].clean(raw_delete_value)
return should_delete
return False
def save_all(self, commit=True):
"""Save all formsets and along with their nested formsets."""
# Save without committing (so self.saved_forms is populated)
# — We need self.saved_forms so we can go back and access
# the nested formsets
objects = self.save(commit=False)
# Save each instance if commit=True
if commit:
for o in objects:
o.save()
# save many to many fields if needed
if not commit:
self.save_m2m()
# save the nested formsets
for form in set(self.initial_forms + self.saved_forms):
if self.should_delete(form): continue
for nested in form.nested:
nested.save(commit=commit)
Notes:
-
I've already looked at the django documentation at https://docs.djangoproject.com/en/dev/topics/forms/formsets/#understanding-the-managementform and haven't found anything helpful that discusses how these values are auto-filled by DJANGO
-
I am using Django V1.5
Questions:
In the absence of POST data and the form is generated exclusively from the database, how should the "form-TOTAL_FORMS" && 'form-INITIAL_FORMS' be filled in correctly to solve this problem?
source to share
Update:
Looking through the example , you provided a snippet there that reads like this at forms.py
the end of the method add_fields()
:
# store the formset in the .nested property
form.nested = [
TenantFormset(data = self.data,
instance = instance,
prefix = 'TENANTS_%s' % pk_value)
]
The argument data
causes problems because it is initially empty, and internally Django determines if the form is bound to a condition, which is similar to this:
self.is_bound = data is not None
# Example
>>> my_data = {}
>>> my_data is not None
True
And since you can see that an empty dictionary in Python is not None, yours is TenantFormset
treated as a form bound
even if it is not. You can fix it like this:
# store the formset in the .nested property
form.nested = [
TenantFormset(data = self.data if any(self.data) else None,
instance = instance,
prefix = 'TENANTS_%s' % pk_value)
]
Could you please post the view and form code and template code for your form?
I am assuming you are not using "control_form" in your template (which adds the "TOTAL_FORMS" and "form-INITIAL_FORMS" form fields you are missing), i.e.
<form method="post">
{{ formset.management_form }}
<table>
{% for form in formset %}
{{ form }}
{% endfor %}
</table>
</form>
source to share