Hi,
Yesterday and today I implemented something and I had a surprise. I'm
explaining here with questions at the end but also in case that I might
save some time to someone working with forms (if someone is going to try
the same approach that I did and it doesn't work).
What I want to do: the user fills in a form (with Django forms using
django-crispy-form). If there is a validation error in a specific field
I need to show a new Field (a checkbox) in order to get confirmation
before the final submission.
Very important: this form is in an InlineFormSet (via inlineformset_factory)
My first reaction: in MyForm.__init__(self) I did something along the lines (pseudocode) of:
"""
if 'amount' in self.errors:
self.fields['allow_error'] = forms.BooleanField()
divs.append('allow_error')
"""
This worked... but it has one problem that some people might have
already spotted! The deletion was not working. kwargs['data'] contained
the 'DELETE' key correctly (with the prefix, etc.). But it was not
available in self.clean_fields when it was trying to be used by Django
in order to decide if the form was going to be deleted.
After some investigation:
self.errors is a property and calls BaseForm.errors which does:
"""
if self._errors is None:
self.full_clean()
return self._errors
"""
(
https://github.com/django/django/blob/master/django/forms/forms.py#L169)
My form is created in BaseFormSet._construct_form method. This does (at the very end):
"""
form = self.form(**defaults)
self.add_fields(form, i)
"""
(
https://github.com/django/django/blob/master/django/forms/formsets.py#L175)
The "add_fields" method adds the "DELETE" in self.fields:
(
https://github.com/django/django/blob/master/django/forms/formsets.py#L390)
It needs to be in self.fields when self.clean_data is populated or it is ignored.
Since in the MyForm.__init__ method I called self.errors and the result
is cached/optimized: 'DELETE' is never added in self.clean_fields (it's
not in self.fields when I called self.errors that triggers the
validation, full_clean, etc; and when it's added in self.fields it's too
late) and then the form never triggers the deletion.
I have some questions:
a) Does it say somewhere in the documents to avoid calling self.errors
from the __init__? I might have missed it. I had some similar problem
using the debugger (via self.errors or self.is_valid perhaps, etc.).
b) I currently now read data from self.data and get the number that I
need and I apply the same logic as the "clean" to decide if I need to
show the checkbox or not. I wonder if there is some other way to
approach this issue that is a bit cleaner (avoiding self.data, self
contained in the form logic, etc.). Do you use any other approach?
c) Actually, should Django tried to prevent my error somehow? :-)
Thanks very much!
--
Carles Pina i Estany