Newforms Custom Fields and Validation
Sun 18 November 2007 by Thejaswi PuthrayaNewforms Custom Fields and Validation
Very often on the Django Users list people ask queries on how to add custom validation to newform fields.
To answer this question in two lines:
- Subclass the field to which you want to add custom validation.
- Override the clean method to add your custom validation.
To give an idea, I will take an example of creating a Pincode (Indian equivalent of ZipCode) field. Before I start coding let me just give you a brief overview about Pincode so that you can understand the logic behind the validation.
Features of Pincode
- The pincode is a 6 digit unique number that identifies the nearest post office in the locality.
(Image taken from Wikipedia. Contents of Wikipedia are licensed under GNU Free Documentation License.)
- From the first two digits we will be able to identify the destination state.
Motive of creating the Pincode Field
Very often people fill forms online that require address with separate fields for State, City and Pincode. Quite often people goof up the pincode (which is the main criterion for correct delivery of mail). The application too (usually) has no means of validating it. Let's create a PinCode Field which also returns the state name to be checked for consistency with the state name entered in the form.
from django import newforms as forms
from django.http import HttpResponse
class PinCodeField(forms.RegexField):
def __init__(self,max_length=None,min_length=None,error_message=None, *args, **kwargs):
super(forms.Regexield,self).__init__('\d{6}',max_length,min_length, *args, **kwargs)
def clean(self,value):
import re
value = super(forms.RegexField, self).clean(value)
if value == u'':
return value
if re.compile('\d{6}').search(value):
if int(value[:2]) == "11":
return ("DE",value)
elif int(value[:2]) in range(30,35):
return ("RAJ",value)
elif int(value[:2]) in range(40,45):
return ("MAH",value)
elif int(value[:2]) in range(45,50):
return ("MP",value)
elif int(value[:2]) in range(50,54):
return ("AP",value)
else:
return None
STATE_NAMES = (
("DE","Delhi"),("RAJ","Rajasthan"), ("MAH","Maharashtra"),
("MP", "Madhya Pradesh"), ("AP","Andhra Pradesh"),
)
# I have entered only 5 names just to show demo. No prejudices involved here!!!
class AddressForm(forms.Form):
state_name = forms.ChoiceField(required = True,choices = STATE_NAMES)
city_name = forms.CharField(max_length = 50)
pincode = PinCodeField()
def index(request):
f = AddressForm()
return HttpResponse("<form action='/sub_form/' method='post'>"+\
f.as_p()+"<p><input type='submit' /></p></form>")
def sub_form(request):
f = AddressForm(request.POST)
if f.is_valid():
if f.cleaned_data['pincode']:
if f.cleaned_data['state_name'] == f.cleaned_data['pincode'][0]:
return HttpResponse("Data Validated")
else:
return HttpResponse("Pin Code not from State")
# Or raise a validation error
else:
return HttpResponse("State not in list.")
# Or raise a validation error
else:
return HttpResponse(f.errors.as_ul())
# Or raise a validation error
This was a perverted example to just show how a newform field alongwith some custom validation be created.
Note
Django provides a field for almost every type of situation. Create a custom field only when you believe you are repeating yourself often.