orthopedics manic buying avodart rate injection cheap bactrim developmental direct order buspar contamination late buy cephalexin now sexual Death cipro no rx cardiopulmonary clomid without prescription strabismus diphtheria clonidine credentialing
Chronosbox

JSONResponse – Working with JSON in Django, easy way.

by 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

:, , , ,

29 Comments for this entry

  • Sérgio Berlotto

    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 !

  • Jim

    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'}]

    • Felipe 'chronos' Prenholato

      Yes, it can work in many ways :) .

      in line

      return JSONResponse(cities.values('id','name'))

      the part cities.values(‘id’,'name’) returns a list of dicts like [{'id':1,'city':'New York'},...].

      but you can do something like that:

      return JSONResponse([
          cities.values('id','name'),
          {
              'status':1,
              'msg':'Sucess'
          }
      ])

      And you should receive your json with any additional data that you want :)

  • Alexandre Santos

    Muito bom cara! ajudou muito!

  • ad

    ¿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)
    ?

  • John M

    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?

  • Nick Young

    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.

  • John M

    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’

  • John M

    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

  • John M

    Opps, posted the wrong github link, i think this one is better

    git://github.com/jfmatth/jsonproject.git

  • Nick Young

    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.

  • Newton

    Hi Felipe, I am trying to return two json objects from two queryset in the same view. How do i go about this?

    • Felipe 'chronos' Prenholato

      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:

       
      &gt;&gt;&gt; serialize_to_json([{"a":1,"b":2,"c":3},{"d":4,"e":5,"f":6}])
       u'[{"a": 1, "c": 3, "b": 2}, {"e": 5, "d": 4, "f": 6}]'

      In javascript you use something like this:

       
      for (i in data) {
           obj = data[i]
           // do your things
      }
  • marcus

    De onde voce ta tirando esse forms, que vc ta importando na view??

    from forms import CityForm

  • marcus

    humm…..tendi, desculpa a pergunta ainda sou noob nesse assunto..

  • marcus

    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??

    • Felipe 'chronos' Prenholato

      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:

       
      # faz de conta que a queryset ModelA.objects.select_related('ModelB').values('user__nome','latitude','longitude') retorne os valores abaixo
      queryset = [
      {"user_nome":"João", "latitude":893, "longitude":612, "id":1 },
       
      {"user_nome":"Maria", "latitude":812, "longitude":601, "id":2 },
       
      {"user_nome":"Josefa", "latitude":987, "longitude":234, "id":3 },
       
      {"user_nome":"Paulo", "latitude":123, "longitude":310, "id":4 },
       
      {"user_nome":"Joelma", "latitude":321, "longitude":431, "id":5 },
       
      {"user_nome":"Rafael", "latitude":234, "longitude":083, "id":6 },
       
      {"user_nome":"Carolina", "latitude":943, "longitude":211, "id":7 },
      ]
       
      return JSONResponse(queryset)

      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/

  • marcus

    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’),

  • marcus

    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”)

  • marcus

    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()

    • Felipe 'chronos' Prenholato

      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&gt 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:

      from aplicativo.models import Cliente
      from jsonresponse import JSONResponse
       
      def grade(request):
      	contacts = Cliente.objects.values() # especificar o valor que vc quer é importante
      	# antes de retorar o JSON faça todo o tratamento que precisar
      	# lembre-se que um objeto json em python é basicmente um dicionário ou uma lista de 
      	# dicionários ou outras listas ou até objetos ...
       
      	# finalmente retorna a lista
      	return JSONResponse(contacts)

      E ai no seu código javascript:

      var grid, store;
      dojo.ready(functon(){
          store = new dojo.data.ItemFileWriteStore({
              url: "/url/da/sua/view/grade/",
              # mais argumentos ... ?
          })
      })

      Tente algo assim que deve funcionar.

  • marcus

    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??

  • Érico

    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

    • Felipe 'chronos' Prenholato

      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):

1 Trackback or Pingback for this entry

Leave a Reply

StatPress

Visits today: 46 Visits since 6 de April de 2009: 87163 Visitors now: %visitoronline%