Posts for json

Django command line interface

Yesterday I started experimenting with a cli interface for a django application.

When you write a CLI you may need to pass complex data (dictionaries, python objects) to the application. So It's not a good idea to break their contents and send them via POST method to the server, it's better if you serialize them first.

On the application side, you have to find that data in the request.POST dictionary, deserialize it, do the things you want to do with it and finally return a json response to the client.

Bits

I wrote a BaseClient class that basically provides a send_request method. You just have to specify the remote action and the arguments to pass, and send_request takes care of the low level bits, POSTs the data and returns a deserialized response from the webserver.

At the server, a middleware detects if a request requires json response and if it's passing some serialized data. This data is binded to request.JSON.

Finally, the view uses a JSONResponse object which is a wrapper around the famous json_encoder by Wolfram Kriesing.

I have packaged all that code in a python module named json_utils so it can be reused.

Writing the application

Now using json_utils implementing a simple cli is pretty straight-forward:

Adding a middleware:

MIDDLEWARE_CLASSES = (
...
    'json_utils.middleware.JSONMiddleware',
...

The NotesClient:

#notes-client.py

#!/bin/env python

from json_utils.client import BaseClient

class NotesClient(BaseClient):
    "A command line client to interact with notes django-app"

    def take_note(self, name, note, tags):
        reqs = dict(title=name,body=note,tags=tags)
        return self._send_request('take_note', reqs)

    def list_notes(self,tags):
        reqs = dict(tags=tags)
        return self._send_request('list_notes',reqs)

if __name__ == "__main__":
    notes_cl = NotesClient('http://localhost:8080/notes/',debug=True)
    params = dict(name='A Demo note', note="Foo", tags=['test', 'demo'])

    notes_cl.take_note(**params)
    res = notes_cl.list_notes(tags=['demo'])
    for n in res['objects']:
        print n['title']

And the views.py serving the client:

#urls.py
urlpatterns = patterns('',
    url(r'^take_note/$', view = 'notes.views.take_note',
        name = 'take_note'
    ),
    url(r'^list_notes/$', view = 'notes.views.list',
        name = 'list'
    ),
)

#views.py
def take_note(request):
    if request.format != 'json':
        raise Http404
    data = request.JSON
    n = Note()
    n.title = data['title']
    n.body = data['body']
    n.tags = ' '.join(data['tags'])

    errors = n.validate()
    if errors:
        return JSONResponse({ 'errors' :errors })
    n.save()
    return JSONResponse({ 'object' : n })

def list(request):
    if request.format != 'json':
        raise Http404
    data = request.JSON
    tags = data['tags']
    notes = Note.tagged.with_all(tags=tags)
    return JSONResponse({'objects' : notes })

Well, that's it! It's the first time I'm messing with json and remote webapps, so I started by visiting djangosnippets.com and got a tone of good ideas about handling json in django. The thing I enjoyed the most is that in a cli-based app you don't have to write any css at all :)

If you want to check out the code, you can find json utils at github. Any feedback is more than welcome :)