Login

JSON view decorator

Author:
kcarnold
Posted:
February 28, 2008
Language:
Python
Version:
.96
Score:
4 (after 4 ratings)

Use this decorator on a function that returns a dict to get a JSON view, with error handling.

Features:

  • response always includes a 'result' attribute ('ok' by default)
  • catches all errors and mails the admins
  • always returns JSON even on errors
 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
44
from django.http import HttpResponse
from django.utils import simplejson
from django.core.mail import mail_admins
from django.utils.translation import ugettext as _
import sys


def json_view(func):
    def wrap(request, *a, **kw):
        response = None
        try:
            response = func(request, *a, **kw)
            assert isinstance(response, dict)
            if 'result' not in response:
                response['result'] = 'ok'
        except KeyboardInterrupt:
            # Allow keyboard interrupts through for debugging.
            raise
        except Exception, e:
            # Mail the admins with the error
            exc_info = sys.exc_info()
            subject = 'JSON view error: %s' % request.path
            try:
                request_repr = repr(request)
            except:
                request_repr = 'Request repr() unavailable'
            import traceback
            message = 'Traceback:\n%s\n\nRequest:\n%s' % (
                '\n'.join(traceback.format_exception(*exc_info)),
                request_repr,
                )
            mail_admins(subject, message, fail_silently=True)

            # Come what may, we're returning JSON.
            if hasattr(e, 'message'):
                msg = e.message
            else:
                msg = _('Internal error')+': '+str(e)
            response = {'result': 'error',
                        'text': msg}

        json = simplejson.dumps(response)
        return HttpResponse(json, mimetype='application/json')
    return wrap

More like this

  1. Template tag - list punctuation for a list of items by shapiromatron 2 months, 2 weeks ago
  2. JSONRequestMiddleware adds a .json() method to your HttpRequests by cdcarter 2 months, 3 weeks ago
  3. Serializer factory with Django Rest Framework by julio 9 months, 2 weeks ago
  4. Image compression before saving the new model / work with JPG, PNG by Schleidens 10 months, 1 week ago
  5. Help text hyperlinks by sa2812 11 months ago

Comments

krid (on June 12, 2008):

This is really useful, but there's one subtle bug. The dict returned from the wrapped function is altered by the wrapper. If the value happens to be something persistent in your application, odd and unexpected things will happen. We have an ajax method to send config data to the client, data that is used elsewhere in the application. I was seeing occasional errors about "result", and couldn't figure it out. I finally tracked it down to this wrapper.

The lines:

response = func(request, *a, **kw)
assert isinstance(response, dict)

Should be changed to:

func_val = func(request, *a, **kw)
assert isinstance(func_val, dict)
response = dict(func_val)

Or you can ditch the assert, and go with:

response = dict(func(request, *a, **kw))

#

mikeshantz (on January 9, 2009):

Very nice! Thanks for this one.

#

sk (on September 28, 2009):

Can I get an example of how to use this? I saved it as 'json_view.py' within my app files, then used 'from project.app.json_view import json_view' at views.py and add '@json_view' at a view function I want to see in JSON, but browser react as a download and the file is a text file with the JSON inside. Is this ok? Can a javascript interact with a downloadable JSON?

#

skarphace (on March 21, 2012):

This has a problem with decimals. As of simplejson 2.1.0, you can use use_decimal=True. So switch this:

json = simplejson.dumps(response)

To this:

json = simplejson.dumps(response, use_decimal=True)

#

Please login first before commenting.