JSON response in Django views with jQuery
By Chee Ming on Feb 21, 2009 | In Random Thoughts, Technical, Exoweb, Python, Django | Send feedback »
I have been looking for a more elegant way to code and structure a Django view so that it will support rendering of responses encoded in either HTML or JSON.
I do this because I want to add some AJAXy usability to my HTML forms while retaining some backwards compatibility for non-Javascript enabled Web browsers.
First, lets look at a simple Django view:
def foo(request):
if request.method == 'POST':
form = Form1(request.POST)
if form.is_valid():
form.save()
return HttpResponseRedirect('/foo/success/')
else:
form = Form1()
return render_to_response('app1/template1.html', {'form': form})
I was lazy and did some hacky stuff like this to support JSON response.
def foo(request):
if request.method == 'POST':
form = Form1(request.POST)
if form.is_valid():
form.save()
if request.POST.get('type', '') == 'json':
data = {} # define your own data structure
return HttpResponse(simplejson.dumps(data),
mimetype='application/json')
else:
return HttpResponseRedirect('/foo/success/')
else:
form = Form1()
return render_to_response('app1/template1.html', {'form': form})
And for the HTML form, I needed some Javascript (using jQuery) like this:
$("#id_cmd_submit").click(function(){
$.post("/foo/", {type: "json"},
function(data) {
alert(data);
}, "json");
});
I was thinking to myself that this type parameter is kinda of ugly and not necessary. The HTTP protocol actually has a Accept header that you can use to define what encoding the HTTP response from the server can be and this is automatically added when you use the $.post() from jQuery using the json type.
If you examine the HTTP headers in the Django view through request.META['HTTP_ACCEPT'] you will see values like this:
application/json, text/javascript, */*
So to make the hacky method a bit less hacky, I did this:
def foo(request):
if request.method == 'POST':
form = Form1(request.POST)
if form.is_valid():
form.save()
if 'application/json' in request.META.get('HTTP_ACCEPT', '')
data = {} # define your own data structure
return HttpResponse(simplejson.dumps(data),
mimetype='application/json')
else:
return HttpResponseRedirect('/foo/success/')
else:
form = Form1()
return render_to_response('app1/template1.html', {'form': form})
And the Javascript now looks like this:
$("#id_cmd_submit").click(function(){
$.post("/foo/", {},
function(data) {
alert(data);
}, "json");
});
Of course the code is still not very pretty and there are better ways to structure it better. In fact, all I did was just to make the API more "conforming" to HTTP and thus the Javascript code slightly cleaner. The Python code is somewhat the same.
As for the Python code, I see that someone has tried to make this style a bit prettier but I am not too sure if it will fit with all my use cases and I don't like the additional complexity.
I did read a bit about Adrian Holovaty's take on this AJAXy stuff. Its definitely cleaner than my approach but I wish Django have better support for HTTP's Accept header since I like the approach of using the Accept header. I found a middleware in Django snippets that can abstract away the complexity a bit in a more elegant way.
There is a good writeup on REST worst practices by Jacob Kaplan-Moss which touches a bit on using the Accept header instead. Malcolum Tredinnick also though on this a bit in an article about content types on this blog.
So I guess I did not bark up the wrong tree. And surprisingly, those blog articles that I found were quite recent. At least I am not outdated. ![]()
No feedback yet
Comments are not allowed from anonymous visitors.
| « Hacking Ubiquity for better Identi.ca/Twitter usage | Cycling: Kuala Lumpur vs Beijing » |
