JSONResponse – Working with JSON in Django, easy way.
by Felipe 'chronos' Prenholato on Sunday, 30 October/2011, under Apps and extensions, Django, python
Nota: Hi my dear readers, despite ChronosBox being a amazing Blog, I’ll focus my efforts on a new blog, with my big friend Handrus Nogueira, so, don’t miss to take a look at Dev With Passion! All ChronosBox posts will be in Dev With Passion, so any content except some new comments will be different
. See ya!
There are dozen of articles and snippets about how work with JSON
and Django, however I saw that most are a bit vague and solution involves use of simplejson to serialize and HttpResponse to send response from the view. There are some details that normally aren’t covered and now I show a easy way and a functional example of how work with Django, JSON and forms.
:: Common problems ::
The most common problems that I see when working with JSON in Django are:
- 1. Isn’t always simple to parse arguments
- 2. The simplejson have problems with some type of objects.
- 3. Why not use a single call to JSONResponse instead of simplejson + HttpRespone
:: The solution ::
- 1. Create a JSON serializer that handle problematic objects to default simplejson.dumps
- 2. Create one JSONResponse, based on HttpResponse with our new serializer
- 3. Easily parse arguments that are sent to get JSON
The JSONResponse uses the JSON serializer and return a response with a JSON object. The argument that are sent to JSONResponse
is the object to be serialized.
With that let’s create a Django APP named jsonui
. This app have 2 models, city and state, 2 views, the main and one that returns json, a simple form and files response.py and utils.py with methods that we use to generate the JSON to response.
:: The JSON serializer ::
The serializer live on utils, splited in a JSONEncoder used by simplejson (he work on ‘problematic’ objects) and a serializer that is a wrapper to simplejson:
Arquivo: jsonui/utils.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | # -*- 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¬ your code about how to handle your type of object here to use when dumping 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) |
:: The JSONResponse ::
The JSONResponse live at response.py, basically, it receive any object (list, dict, QuerySet) and returns a response object with json serialized:
Arquivo: jsonui/response.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | # -*- 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) |
:: Putting to work in view. ::
Without further ado, let’s see how it works. We have two views:
- mainview: call the form and render a template with javascript code used to execute the POST and render JSON sent by json_get_city view as options of select city.
- json_get_city: return a queryset of cities jsonified.
Arquivo: urls.py @ 17
17 18 | (r'^$','jsonui.views.mainview'), (r'^ajax/city/','jsonui.views.json_get_city'), |
Arquivo: jsonui/views.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | # -*- 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')) |
The qdct_as_kwargs method is responsible by return a dict that is sent to filter method of any model from request.POST or request.GET. That’s a nice magic very useful to views like this one.
So, as soon as url /ajax/city/ receive the POST, we consult model filter using qdct_as_kwargs and JSONResponse returns a JSON ready to use in javascript.
Arquivo: jsonui/utils.py @ 44
44 45 46 47 48 | def qdct_as_kwargs(qdct): kwargs={} for k,v in qdct.items(): kwargs[str(k)]=v return kwargs |
The javascript responsible to send POST live in template base.html used by mainview. That’s relevant snippet:
Arquivo: templates/base.html @ 10
10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | $(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("<option selected=\"selected\" value=\"\">------</option>") for (i in data) { i = data[i] $("#id_city").append( "<option value=\""+i.id+"\">"+i.name+"</option>" ) } }) }) }) |
This code execute a POST to /ajax/city/ and on receive a response fill a select with cities received as json.
:: Finishing::
Creating similar javascript code and using the qdct_as_kwargs and JSONResponse methods, like in view json_get_city you can easily create a lot of views or maybe a more generic view that get data from any model. Just let the imagination run
.
You can download entire project in these links: jsonproject.tbz2 (MD5 Hash) (mirror, mirror MD5)
I built using Django 1.2 SVN, but runs in any version >= 1.0.
Have fun, contribute, say about
See ya!
** UPDATES
- If you get 403 errors, check ajax view permissions and CSRF (tip by Nick Young)
- If you need additional data in JSON response check for my reply to Jim comment
29 Comments for this entry
1 Trackback or Pingback for this entry
-
Mais um modo de trabalhar com JSON e Django « Burning Code
Thursday June 14th, 2012 on 09:53 PM[...] http://chronosbox.org/blog/jsonresponse-in-django Share this:TwitterFacebookGostar disso:GostoBe the first to like this. [...]
Thursday May 6th, 2010 on 03:22 PM
Cara, muito bom isto !
Eu acabei juntando todas as soluções de json em meu projeto e criando uma solução meio “frankenstein” hahehehe
Vou testar esta sua solução no meu projeto …
Valeu !
Tuesday September 28th, 2010 on 08:06 PM
does this work for returning a data structure containing a queryset and a dict? for example to return this as json data : [ cities, {'status': 1, 'msg': 'Success'}]
Tuesday September 28th, 2010 on 08:26 PM
Yes, it can work in many ways
.
in line
the part cities.values(‘id’,'name’) returns a list of dicts like [{'id':1,'city':'New York'},...].
but you can do something like that:
And you should receive your json with any additional data that you want
Saturday November 6th, 2010 on 02:00 PM
Muito bom cara! ajudou muito!
Thursday May 5th, 2011 on 12:54 PM
¿que debo hacer con el MD45?.
ya hice mi proyecto y en la url le pongo http://localhost:8000/ajax/city pero me genera un .part lo habro como texto plano, y no sale nada.
¿Qué hago con esto (r’^$’,mainview)
?
Sunday May 22nd, 2011 on 02:53 AM
MD5 files is just to check integrity of package, you can check on windows (http://www.softpedia.com/get/System/File-Management/MD5-Check.shtml) or linux (https://help.ubuntu.com/community/HowToMD5SUM) (or yet other OS).
About your error, the ajax url should be handled via javascript, so you can use the data.
Normally if opened in browser, a JSON file will be downloaded, you can use some plugins to see JSON instead download it:
Firefox: https://addons.mozilla.org/pt-br/firefox/addon/jsonview/
Chrome: https://chrome.google.com/webstore/detail/chklaanhfefbnpoihckbnefhakgolnmc
And, to finish, (r’^$’,mainview) indicates that your url pointin to / will call mainview. Doc about that is present at Django basics: http://docs.djangoproject.com/en/dev/intro/tutorial03/?from=olddocs#design-your-urls
Thursday May 12th, 2011 on 08:34 AM
Muito legal!!!
Vlw
Thursday June 30th, 2011 on 12:51 PM
Thank you for this, its simple enough to get me going, however..
I tried your example and can’t seem to get the ajax part to work.
I’m running manage.py runserver, and when I pull down a city, I get a 403 2332 error and the States combo is no populated.
Thoughts?
Thursday June 30th, 2011 on 02:14 PM
Hello John. The 403 code is for access denied / forbidden. Something is breaking you ajax when validate access.
Thursday June 30th, 2011 on 09:17 PM
John, I received the same 403 error that you did. The CSRF protection is causing the 403. In views.py add the @csrf_exempt tag to json_get_city and the 403 will go away.
Felipe, thanks for posting this. I was looking all around for a tutorial on how to easily add JSON Responses to a site.
Thursday June 30th, 2011 on 10:06 PM
Thx for share! I’ll update the post with one link to this comment
Friday July 1st, 2011 on 04:58 PM
Nick, thanks for the reply, after talking with my friend who had the same issue, we came to the same conclusion and i was able to fix with both the middleware removal and the crsf_exempt stuff.
Now I’m trying to add the jQuery stuff to post the crsf cookie on the post, i’ve seen lots of posts on this.
This post is awesome to get me going with Ajax on django, thanks again Felipe’
Friday July 1st, 2011 on 08:12 PM
I’ve added this to my github account, and you’re all welcome to pull it down. It’s been ‘fixed’ for the CSRF stuff.
It would be great to get some updates to it from everyone on the list. I’m not sure how that works on github, but if u know, tell me and i’ll accept any pushes.
John
https://github.com/jfmatth/jsonproject
Friday July 1st, 2011 on 08:16 PM
Opps, posted the wrong github link, i think this one is better
git://github.com/jfmatth/jsonproject.git
Monday July 18th, 2011 on 09:49 PM
John, I don’t see why having the JSON call be CSRF exempt is a problem. The JSON call is retrieving information from the database, but not updating it. The CSRF is required only when there is an update being made to the server.
If you are going to update the database, a POST should be submitted to the server, in which case the submit button on the CSRF protected form would suffice.
Let me know if there is something that I am missing here.
Tuesday November 1st, 2011 on 08:08 AM
Hi Felipe, I am trying to return two json objects from two queryset in the same view. How do i go about this?
Friday November 4th, 2011 on 10:03 AM
You can put objects in a list, ex:
I have {“a”:1,”b”:2,”c”:3} and {“d”:4,”e”:5,”f”:6} and can return [{"a":1,"b":2,"c":3},{"d":4,"e":5,"f":6}]. Se how it appears on on terminal with serialize_to_json:
In javascript you use something like this:
Thursday November 3rd, 2011 on 05:20 PM
De onde voce ta tirando esse forms, que vc ta importando na view??
from forms import CityForm
Friday November 4th, 2011 on 10:05 AM
Esse CityForm é um ModelForm, fica em jsonui.forms. É só um exemplo
Friday November 4th, 2011 on 06:53 PM
humm…..tendi, desculpa a pergunta ainda sou noob nesse assunto..
Friday November 4th, 2011 on 06:58 PM
Eu implementei dois models A e B e tenho que fazer com que uma grid carregue pela URL os dados em .json
{“grade” : [
{"nome":"João", "latitude":893, "longitude":612, "id":1 },
{"nome":"Maria", "latitude":812, "longitude":601, "id":2 },
{"nome":"Josefa", "latitude":987, "longitude":234, "id":3 },
{"nome":"Paulo", "latitude":123, "longitude":310, "id":4 },
{"nome":"Joelma", "latitude":321, "longitude":431, "id":5 },
{"nome":"Rafael", "latitude":234, "longitude":083, "id":6 },
{"nome":"Carolina", "latitude":943, "longitude":211, "id":7 },
]};
eu nao estou conseguindo carregar a extensao .json ele tem que ler e carregar a grid pelo dojo url =”grade”
EU tentei implementar esse metodo seu de serialização, mas como ainda sou noob estou me perdendo, teria alguma dica??
Sunday November 6th, 2011 on 08:31 PM
O meu JSONResponse retorna como tipo JSON, se eu não me engano o dojo vai precisar só do que vc atribuiu a ‘grade’, ou você pode ter uma url qualquer que aponte para a view que retorne o JSON e na view:
Com isso no seu javascript você só precisa pegar os dados retornados da sua url e mandar pro dojo. Vi algo parecido aqui: http://www.enterprisedojo.com/2011/01/31/a-simple-dojo-datagrid-example-or-so-close-yet-wide-right/
Monday November 7th, 2011 on 06:02 PM
Cara, faz muito sentido isso, mas onde que eu devo colocar o arquivo dados.json para ele ser chamado e carregar a extensao??
eu Reajustei o direotiro de arquivos estaticos
STATICFILES_DIRS = (
‘/home/marcus/Templates/static/’,
)
e na grid esta assim:
var grid, store;
dojo.ready(function(){
store = new dojo.data.ItemFileWriteStore({
url: “carregar”//essa url que passa para a view carregar os dados
a urls.py do projeto esta assim:
urlpatterns = patterns(”,
url(r’^grade/’, include(‘Aplicativo.urls’)),
url(r’^admin/’, include(admin.site.urls)),
)
a urls.py do aplicativo esta assim:
(r’^$’, ‘index’),
#(r’^(?P\d+)/$’, ‘detail’),
#(r’^(?P\d+)/results/$’, ‘results’),
#(r’^(?P\d+)/vote/$’, ‘vote’),
#(r’^static/(.*)$’ ,’static.serve’,{‘document_root’:'settings.STATIC_ROOT’}),#arquivo estatico
(r’^(?P\d+)/grade/$’, ‘grade’),
Monday November 7th, 2011 on 06:04 PM
A view do aplicativo esta dessa forma:
#serializador
def grade(request):
json_serializer = serializers.get_serializer(“json”)()
json_serializer.serialize(queryset, ensure_ascii=False, stream=response)
all_contacts = list(iter(Cliente.objects.values()))
json_contacts = simplejson.dumps(all_contacts)
return HttpResponse(json_contacts, mimetype=”application/json”)
Monday November 7th, 2011 on 06:07 PM
O models esta disposto dessa forma:
from django.db import models
import datetime
class A(models.Model):
nome = models.CharField(max_length=200)
data = models.DateTimeField()
def Data(self):
return self.data.date() == datetime.date.today()
class B(models.Model):
a = models.ForeignKey(A)
nome = models.CharField(max_length=200)
latitude = models.IntegerField()
longitude = models.IntegerField()
data = models.DateTimeField()
def Nome(self):
return self.nome
def Latitude(self):
return self.latitude
def Longitude(self):
return self.longitude
def Data(self):
return self.data.date() == datetime.date.today()
Monday November 7th, 2011 on 11:44 PM
Marcus, ficou meio confuso todo esse código colado, eu recomendo a você usar o http://gist.github.com daqui para frente
. Você também pode usar <pre lang=’python’>…</pre> aqui no blog, variando a linguagem usada claro
. Fica mais fácil de lermos.
Eu imagino que com esse arquivo dados.json você deve estar falando da view que retorna o JSON.
Nesse caso a sua view seria a view grade, só que bem mais simples, veja:
E ai no seu código javascript:
Tente algo assim que deve funcionar.
Wednesday November 9th, 2011 on 05:37 PM
Entao, em primeiro lugar obrigado pela resposta rapida, eu dei uma treinada aqui e implementei alguns templates novos no novo projeto. meus models sao respectivcamente Empresa e Pessoa. estou usando o banco sqlite3 para inserção dos dados o admin funciona perfeitamente, as views apresentam a arquitetura CRUD, elas inserem, listam.blabla blá, eu adaptei um arquivo estatico que puxa o template do dojo que segue esse caminho projeto/aplicativo/static/empresa/dojo/grid_map.html
A serialização para json deve ser feita quando eu estou incluindo os dados dentro do banco correto?..e como eu consigo recuperar esses dados? eu aponto a url para a view e faço o q???
minha view de inserção esta simples
Como eu faço para recuperar esses dados??
Wednesday October 17th, 2012 on 02:24 AM
Bom dia,
fora do admin rodei normalmente.
Existe uma soluçõa(link com um how to) para fazer esse tipo de implementação dentro do admin?
Obrigado e abraços
Wednesday October 17th, 2012 on 08:42 AM
Se eu entendi você quer retornar JSON dentro do Admin? É isso? Que eu saiba não.
Entretanto você pode configurar suas próprias views (vide django-selectable) para retornar JSON e simplesmente usa-las direto do admin.
Você também pode alterar as views de um ModelAdmin, veja quais são:
$ grep "def.*_view" lib/python2.7/site-packages/django/contrib/admin/options.py
def add_view(self, request, form_url='', extra_context=None):
def change_view(self, request, object_id, form_url='', extra_context=None):
def changelist_view(self, request, extra_context=None):
def delete_view(self, request, object_id, extra_context=None):
def history_view(self, request, object_id, extra_context=None):