I’ve spent the past six months working on a lot of different projects but one of the biggest projects I’ve been working on is a back-end system for the company I work for. If you ever want a sure-fire way to begin eating, breathing, and sleeping in Python, try joining a start-up and building something like this from the ground up. Lucky for us, Django is around to make this sort of thing far less difficult.
One of the greatest features of Django is the built in form handling functionality. Define some fields, throw a variable at the template, and add a form tag and submit button and you are pretty much done. It even does all the validation for you. Well, sort of…
As all generic things go, the validation that Django does is, well, generic. It’s easy to override the methods that do the validation, but then you lose the original validation provided by Django. So what do you do when you want to add to the existing validation instead of replace it? It isn’t exactly documented but it is possible…
In this example we have a form with a field that is only required in certain circumstances. Everything else about the validation needed to be the same.
Define the form:
class CreditCardPaymentForm(forms.Form):
mark_paid_in_full = forms.BooleanField(required=False)
payment_note = forms.CharField(required=False)
In this form you will have a checkbox to mark the payment “paid in full.” What we want is to require a note when that checkbox is selected. I’ve omitted the rest of the form as it’s irrelevant for the example.
This is where the trickery comes in. We are going to override the clean() method, check if mark_paid_in_full is selected, and change payment_note to required if it is, then run the existing validation.
def clean(self):
if self.cleaned_data['mark_paid_in_full']:
payment_note_field = self.fields['payment_note']
payment_note_field.required = True
try:
super(forms.CharField, payment_note_field).clean(
self.data['payment_note'])
except forms.ValidationError, error:
self._errors['payment_note'] = error.messages
return self.cleaned_data
As you can see a few things happen here. First it’s important to know that self.fields will contain all the field objects. Then we try to call the CharField’s clean method. I won’t delve into the specifics of how super works as there are already many articles covering that topic but if you aren’t familiar with super, it’s worth reading about. Then if the clean method raises a ValidationError, catch it and put it in the _errors property. You could just as easily leave the ValidationError raised and Django would print the error message… albeit at the top of the form. In some cases this might be desirable, but here we want to actually tie those errors to the appropriate field. Finally, you should always return self.cleaned_data or you will get some truly bizarre results
if this is something you are trying to do I hope this saved some time.