Existem por ai dezenas de artigos e snippets sobre como trabalhar com JSON em Django, entretando vi que a maioria é um pouco vaga e a solução envolve serializar com simplejson e mandar pro HttpResponse. Existem alguns detalhes que normalmente não se cobrem e eu apresento a maneira fácil de trabalhar e um exemplo funcional de JSON em um formulário.
Arquivo: jsonui/utils.py
# -*- coding: utf-8 -*-
from django.utils import simplejson
from django.utils.encoding import force_unicode
from django.db.models.base import ModelBase
class LazyJSONEncoder(simplejson.JSONEncoder):
""" a JSONEncoder subclass that handle querysets and models objects.
Add how handle your type of object here to use when when dump json"""
def default(self,o):
# this handles querysets and other iterable types
try:
iterable = iter(o)
except TypeError:
pass
else:
return list(iterable)
# this handlers Models
try:
isinstance(o.__class__,ModelBase)
except Exception:
pass
else:
return force_unicode(o)
return super(LazyJSONEncoder,self).default(obj)
def serialize_to_json(obj,*args,**kwargs):
""" A wrapper for simplejson.dumps with defaults as:
ensure_ascii=False
cls=LazyJSONEncoder
All arguments can be added via kwargs
"""
kwargs['ensure_ascii'] = kwargs.get('ensure_ascii',False)
kwargs['cls'] = kwargs.get('cls',LazyJSONEncoder)
return simplejson.dumps(obj,*args,**kwargs)
Arquivo: jsonui/response.py
# -*- coding: utf-8 -*-
from utils import serialize_to_json
from django.http import HttpResponseForbidden, HttpResponse
class JSONResponse(HttpResponse):
""" JSON response class """
def __init__(self,content='',json_opts={},mimetype="application/json",*args,**kwargs):
"""
This returns a object that we send as json content using
utils.serialize_to_json, that is a wrapper to simplejson.dumps
method using a custom class to handle models and querysets. Put your
options to serialize_to_json in json_opts, other options are used by
response.
"""
if content:
content = serialize_to_json(content,**json_opts)
else:
content = serialize_to_json([],**json_opts)
super(JSONResponse,self).__init__(content,mimetype,*args,**kwargs)
Arquivo: urls.py @ 17
(r'^$','jsonui.views.mainview'),
(r'^ajax/city/','jsonui.views.json_get_city'),
Arquivo: jsonui/views.py
# -*- coding: utf-8 -*-
from django.shortcuts import render_to_response
from django.template import RequestContext
from models import City,State
from forms import CityForm
from utils import qdct_as_kwargs
from response import JSONResponse
def mainview(request):
return render_to_response('base.html',{'form': CityForm() },
context_instance=RequestContext(request))
def json_get_city(request):
if not request.method == "POST":
# return all cities if any filter was send
return JSONResponse(City.objects.order_by('name'))
# get cities with request.POST as filter arguments
cities = City.objects.filter(**qdct_as_kwargs(request.POST)).order_by('name')
#return JSONResponse with id and name
return JSONResponse(cities.values('id','name'))
O método qdct_as_kwargs é responsável por retornar um dicionário que é passado parao método filter de um objeto a partir do request.POST ou request.GET. É uma magica legal que agiliza e muito a criação de views como essa.
Arquivo: jsonui/utils.py @ 44
def qdct_as_kwargs(qdct):
kwargs={}
for k,v in qdct.items():
kwargs[str(k)]=v
return kwargs
Assim que a url /ajax/city/ recebe o POST ela obtem as cidades usando o qdct_as_kwargs e retorna a JSONResponse, no javascript a resposta é um JSON pronto para usar.
O javascript responsável por mandar o POST esta no template base.html que é renderizado pela mainview. Este é o trecho relevante:
Arquivo: templates/base.html @ 10
$(function(){
$("#id_state").change(function(e){
$.post(
// url to post
"/ajax/city/",
// args
{state__id:$(this).val()},
// response callback
function(data,text,xhrobject){
// uncomment if you wanna to see objects on firebug console
//console.log(data,text,xhrobject)
$("#id_city").children().remove()
.append("")
for (i in data) {
i = data[i]
$("#id_city").append(
""
)
}
})
})
})
Este código executa um POST para /ajax/city/ e na volta preenche o select de cidades com o objeto retornado.