Django Form Validation and Ajax
Ready to ajaxify your django forms? Doing so with simple forms can be a breeze, just submit the form, check for is_ajax(), return an HttpResponse. Where it all falls apart is when you have multiple fields and multiple validation type of errors. You could write all the logic yourself on the front end javascript or you could get it for free with django forms. The point of this post is to show you how to do error handling using only built in django features and jquery.
The Problem with Django Form Errors and Ajax
Let’s say we have the following model form:
class PerformanceGoalForm(forms.ModelForm):
class Meta:
model = Goal
fields = (
'from_threshold',
'to_threshold',
'measure',
'name',
'end_date'
)
In our view we can process the form like so:
if request.method == "POST":
# In your view that is processing the form request
if request.is_ajax():
if form.is_valid():
goal = form.save()
# Serialize the goal in json format and send the
# newly created object back in the reponse
# have to put it in a list even if it's just one instance
data = serializers.serialize('json', [goal,])
else:
# Since form.errors is a proxy need to create a dict from it with unicode
data = json.dumps(dict([(k, [unicode(e) for e in v]) for k,v in form.errors.items()]))
return HttpBadRequestResponse(data, mimetype='application/json')
return HttpResponse(data, mimetype='application/json')
The problem is when there are errors. If you try to serialize the form.errors it won’t be recognized as a json object. Why? Because form.errors is actually a proxy that contains objects for errors so it can intelligently return the errors in HTML. You’ll notice this if you try to print form.errors in your view.
Now what?
We need to serialize form.errors to return a dictionary of errors back to the ajax call. Dump the errors like so:
# FYI I've imported simplejson as json
data = json.dumps(dict([(k, [unicode(e) for e in v]) for k,v in form.errors.items()]))
OK Cool, But the error callback function keeps returning a string!
Unfortunately, the ajax error function will only return a string and not a json object like you want it to. It’s called textStatus for a reason. We want a dictionary so we can map it to the fields that have errors. To get around it, return an HttpResponse instead of HttpBadRequestResponse (that’s a 200 http response code rather than a 400) with our serialized form.errors, but put it all in a dictionary with a single key ‘errors.’ That way on the success callback function you can check for errors and do something about it, otherwise do your normal success stuff. We’re doing this because the success callback will accept a json object. This simple little hack is all we need to get full form validation with django forms using ajax.
Rewrite the function to return an HttpResponse with your form errors in a dictionary with the key ‘errors.’
data = json.dumps({'errors': dict([(k, [unicode(e) for e in v]) for k,v in form.errors.items()])})
# In your view that is processing the form request
if request.is_ajax():
if form.is_valid():
goal = form.save()
# Serialize the goal in json format and send the
# newly created object back in the reponse
data = serializers.serialize('json', [goal,])
else:
# Since form.errors is a proxy need to create a dict from it with unicode
data = json.dumps(dict([(k, [unicode(e) for e in v]) for k,v in form.errors.items()]))
return HttpResponse(data, mimetype='application/json')
Now in our ajax call we can do something like this:
success = function(success){
if (success.errors){
$('#error').show();
var fieldNames = new Object();
for (error in success.errors){
//Do something with the error dict like map it to the field's div and turn it red
}
}
And there you have it. Out of the box django form validation with ajax! There are some libraries out there that do this with template tags and additional validation views, but I feel this approach is more explicit.
5 Notes/ Hide
-
e-mechanism liked this
-
nickthejam liked this
-
zeb liked this
-
alexkehayias posted this