The django docs cover cleaning and validating FIELDS that depend on each other, but I can't find anything that covers forms that depend on each other.
I have a single HTML form with which contains both a standard django form and a django formset. Proper validation of each form in the formset is entirely conditional based on a value from the main form (e.g. check a box on the main form, and a specific field on each form in the formset suddenly becomes required).
My intuition is to "simply" pass the entire main form into the formset validation call, like so:
def my_view(request):
MyFormSet = formset_factory(MyForm, extra=2, can_order=True)
if request.method == 'POST':
form = MainForm(request.POST)
formset = MyFormSet(request.POST)
if form.is_valid() and formset.is_valid(form): # <-- ?!?!
# The formset is now validated based on the form
However, to make that work, I believe I would have to override both the formset is_valid()
along with the underlying form is_valid()
and clean()
method. So, it gets pretty messy pretty quick.
Is there a better way to do this?
Form validation happens when the data is cleaned. If you want to customize this process, there are various places to make changes, each one serving a different purpose. Three types of cleaning methods are run during form processing. These are normally executed when you call the is_valid() method on a form.
The is_valid() method is used to perform validation for each field of the form, it is defined in Django Form class. It returns True if data is valid and place all data into a cleaned_data attribute.
cleaned_data is where all validated fields are stored.
{{ form.as_p }} – Render Django Forms as paragraph. {{ form.as_ul }} – Render Django Forms as list.
I investigated doing something like this once, and this tutorial http://yergler.net/blog/2009/09/27/nested-formsets-with-django/ was fairly helpful.
Another way to do this is:
def my_view(request):
MyFormSet = formset_factory(MyForm, extra=2, can_order=True)
if request.method == 'POST':
form = MainForm(request.POST)
formset = MyFormSet(request.POST, other_form = form)
if form.is_valid() and formset.is_valid(): # <-- ?!?!
# The formset is now validated based on the form
Then
class MyFormSet(...):
def __init__(self, *args, **kwargs):
if kwargs.has_key('other_form'):
self.myformforlater = kwargs.pop('other_form')
Super(MyFormSet, self).__init__(*args, **kwargs)
This way you only have to override the init method, and you have access to the outer form from any validation step.
Here's the code I ended up with, using Ted's answer (django 1.3):
class BaseMyFormSet(BaseFormSet):
main_form = None
def __init__(self, *args, **kwargs):
# Save the main form until validation
if kwargs.has_key('main_form'):
self.main_form = kwargs.pop('main_form')
super(BaseMyFormSet, self).__init__(*args, **kwargs)
def clean(self):
if any(self.errors):
# Don't bother validating the formset unless each
# form is valid on its own
return
checkbox = self.main_form.cleaned_data['my_checkbox']
if checkbox:
for form in self.forms:
# Do some extra validation
def my_view(request):
MyFormSet = formset_factory(MyForm, extra=2, can_order=True,
formset=BaseMyFormSet)
if request.method == 'POST':
form = MainForm(request.POST)
formset = MyFormSet(request.POST, main_form=form)
if form.is_valid() and formset.is_valid():
# The formset is now validated based on the form
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