JSON view decorator

 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

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

#

(Forgotten your password?)

You may use Markdown syntax here, but raw HTML will be removed.