An annoyance of the development process for frontend teams consuming a JSON-based API is not getting a JSON-formatted response back when an error occurs. Getting Django's HTML-based error pages instead of something formed in JSON means more coding at their end to capture errors and top that off, the HTML itself is long and difficult to eyeball quickly.

Here's what I'm looking to replace:

$ curl -s localhost:8001/missing <!DOCTYPE html> <html lang = "en" > <head> <meta http-equiv = "content-type" content = "text/html; charset=utf-8" > <title>Page not found at /missing</title> <meta name = "robots" content = "NONE,NOARCHIVE" > <style type = "text/css" > html * { padding:0 ; margin:0 ; } body * { padding:10px 20px ; } body * * { padding:0 ; } body { font:small sans-serif ; background:#eee ; } body>div { border-bottom:1px solid #ddd; } h1 { font-weight:normal ; margin-bottom:.4em ; } h1 span { font-size:60% ; color:#666 ; font-weight:normal ; } table { border:none ; border-collapse: collapse ; width:100% ; } td, th { vertical-align:top ; padding:2px 3px ; } th { width:12em ; text-align:right ; color:#666 ; padding-right:.5em ; } #info { background:#f6f6f6; } #info ol { margin: 0.5em 4em; } ...

Being able to return both regular content and unexpected errors in JSON format helps remove the pains of dealing with errors during the development process.

django-jsonview is a package that gives you a decorator for your view methods that wraps their contents in a JSON response. It will also catch exceptions and return them in a consistent fashion and there is even support to modify HTTP headers if need be.

I'll walk you through it's usage.

Setup a Django project and application First, I'll create a new project and app. The project will be called 'speak_json' and the app within it will be called 'photos': $ pip install Django == 1 .7 django-jsonview $ django-admin startproject speak_json $ cd speak_json $ django-admin startapp photos Now I need to edit (and in some cases create) five files: The base settings file, the base urls file, a models file with an example model, the urls file for the app and finally, the view with our decorated views. I added 'photos' to the INSTALLED_APPS tuple in speak_json/settings.py : INSTALLED_APPS = ( 'django.contrib.admin' , 'django.contrib.auth' , 'django.contrib.contenttypes' , 'django.contrib.sessions' , 'django.contrib.messages' , 'django.contrib.staticfiles' , 'photos' , ) I added an entry for urls that begin with 'photos/' to route to the photos app in speak_json/urls.py : urlpatterns = patterns ( '' , url ( r '^photos/' , include ( 'photos.urls' , namespace = 'photos' )), url ( r '^admin/$' , include ( admin . site . urls )), ) I created a small model in photos/models.py : from django.db import models class Person ( models . Model ): first_name = models . CharField ( max_length = 30 ) last_name = models . CharField ( max_length = 30 ) After I created the model I created the migration and migrated the app so the sqlite3 database would have the corresponding table: $ python manage.py makemigrations $ python manage.py migrate I then added in URL mappings for five views in photos/urls.py : from django.conf.urls import patterns , url from . import views urlpatterns = patterns ( '' , url ( r '^foo_bar/$' , views . foo_bar ), url ( r '^is_payment_needed/$' , views . is_payment_needed ), url ( r '^server_name/$' , views . server_name ), url ( r '^one_equals_two/$' , views . one_equals_two ), url ( r '^missing_person/$' , views . missing_person ), ) Just to keep track, these are the files in their respective locations for this project: $ find speak_json/ speak_json/db.sqlite3 speak_json/manage.py speak_json/photos/__init__.py speak_json/photos/admin.py speak_json/photos/migrations speak_json/photos/migrations/0001_initial.py speak_json/photos/migrations/__init__.py speak_json/photos/models.py speak_json/photos/tests.py speak_json/photos/urls.py speak_json/photos/views.py speak_json/speak_json speak_json/speak_json/__init__.py speak_json/speak_json/settings.py speak_json/speak_json/urls.py speak_json/speak_json/wsgi.py

Wrapping views with the json_view decorator The fifth file I edited was photos/views.py which is where I added in five view methods wrapped with the json_view decorator: from jsonview.decorators import json_view from .models import Person @json_view def foo_bar ( request ): return { 'foo' : 'bar' , } @json_view def is_payment_needed ( request ): if request . user and not request . user . is_authenticated (): # Send a 402 Payment Required status. return { 'subscribed' : False }, 402 # Send a 200 OK. return { 'subscribed' : True } @json_view def server_name ( request ): return {}, 200 , { 'X-Server' : 'myserver' } @json_view def one_equals_two ( request ): assert 1 == 2 , '1 is not equal to 2' @json_view def missing_person ( request ): person = Person . objects . get ( pk = 1234 ) return { 'found' : True , 'person_id' : person . pk }