Version 14 (modified by anonymous, 4 years ago) (diff)



This is an easy-to-use implementation of a JSON-RPC handler for Django. (A second alternative implementation which is even easier to use - but has less configuration options - is also included, below)


Example Django Usage


from jsonrpc import JsonRpc, publicmethod
from django.core.urlresolvers import reverse
from django.http import HttpResponse

class MyRpcMethods(object):

    url = reverse("myapp-rpc")

    def add(x, y):
        return x+y

    def sub(x, y):
        return x-y

    def read_diary(self):
        return "Here's all my secrets ..."

    def find_person(attribs):

        filters = dict((key, val) for key, val in attribs.items()
                       if key in ("first_name", "last_name"))

        return [
            {"first_name": p.first_name, "last_name": p.last_name} \
            for p in Person.objects.filter(**filters)

     def sayHello(*args):
         return "hello "

# set the urls /myapp/rpc/ to this myapp.views.my_rpc_view and give it a name='myapp-rpc'
# looks like this url(r'^/myapp/rpc/$', 'myapp.views.my_rpc_view', name='myapp-rpc'),
# i cannot use the result give by this example, i use 
# return HttpResponse(result, mimetype='application/json')
def my_rpc_view(request):

    rpc     = JsonRpc( MyRpcMethods() )
    result  = rpc.handle_request(request)

    return result

Example Javascript client usage

var rpc = new dojo.rpc.JsonService("/myapp/rpc/");

rpc.add(3,4).addCallback(function(result) {
>>> 7

rpc.callRemote("sub", [9,5]).addCallback(function(result) {
>>> 4

rpc.callRemote("read_diary").addCallback(function(secrets) {
>>> no such method

rpc.find_person({last_name: "Baggins"}).addCallback(function(result) {
    dojo.forEach(result, function(person) {
      console.log("Found: " + person.first_name, person.last_name);
>>> Found: Bilbo Baggins
>>> Found: Frodo Baggins


# Copyright (c) 2009, Ben Wilber (
# All rights reserved
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License. You should have
# received a copy of the GPL license along with this program; if you
# did not, you can find it at

class publicmethod(object):

    def __init__(self, method):
        self.method = method
        __public__  = True

    def __call__(self, *args, **kwargs):
        return self.method(*args, **kwargs)

    def get_args(self):
        from inspect import getargspec
        return [ a for a in getargspec(self.method).args if a != "self" ]

class JsonRpc(object):

    def __init__(self, instance, allow_errors=True, report_methods=True):

        self.instance            = instance
        self.allow_errors        = allow_errors
        self.report_methods        = report_methods

        if not hasattr(self.instance, "url"):
            raise Exception("'url' not present in supplied instance")

    def get_public_methods(self):

        return [
            m for m in dir(self.instance) if \
            getattr(self.instance, m).__class__.__name__ == "publicmethod" and \
            getattr(self.instance, m).__public__ == True

    def generate_smd(self):

        smd = {
            "serviceType": "JSON-RPC",
            "serviceURL": self.instance.url,
            "methods": []

        if self.report_methods:
            smd["methods"] = [
                {"name": method, "parameters": getattr(self.instance, method).get_args()} \
                for method in self.get_public_methods()

        return simplejson.dumps(smd)
    def dispatch(self, method, params):

        if hasattr(self.instance, "dispatch") and \
            return self.instance.dispatch(method, params)
        elif method in self.get_public_methods():
            return getattr(self.instance, method)(*params)
            return "no such method"

    def serialize(self, raw_post_data):

        raw_request        = simplejson.loads(raw_post_data)
        request_id        = raw_request.get("id", 0)
        request_method    = raw_request.get("method")
        request_params    = raw_request.get("params", [])

        response        = {"id": request_id}

            response["result"] = self.dispatch(request_method, request_params)
            if self.allow_errors:
                from sys import exc_type, exc_value
                response["error"] = "%s: %s" % (exc_type, exc_value)
                response["error"] = "error"

        return simplejson.dumps(response)

    def handle_request(self, request):

        if request.method == "POST" and \
            len(request.POST) > 0:
            return self.serialize(request.raw_post_data)
            return self.generate_smd()

Alternative JsonRpc Implementation

This is the proven and working JSON-RPC Django handler developed incrementally since 2007 and deployed in production Pyjamas Web 2.0 applications. It is much shorter, simpler, easier to use and less cumbersome than the above, although it does not have support for GET (to view a list of methods) and does not have the same level of configurability. Importantly, the original request object is passed to all functions in the JSONRPC service, thus allowing the application to gain access to Django session information, and cookies etc.

Usage examples are included inline in the source. Note in particular that the JSONRPCService class instance itself is handed to urlpatterns, and so all POST requests result in JSONRPCService's __call__ method being called, resulting in a much simpler approach than the above.

This code is maintained at and a version is included in

#   original code:
#   also from:
from django.utils import simplejson
import sys

# JSONRPCService and jsonremote are used in combination to drastically
# simplify the provision of JSONRPC services.  use as follows:
# from jsonrpc import JSONRPCService, jsonremote
# jsonservice = JSONRPCService()
# @jsonremote(jsonservice)
# def test(request, echo_param):
#     return "echoing the param back: %s", echo_param
# then dump jsonservice into urlpatterns:
#  (r'^service1/$', 'djangoapp.views.jsonservice'),

def response(id, result):
    return simplejson.dumps({'version': '1.1', 'id':id,
                             'result':result, 'error':None})
def error(id, code, message):
    return simplejson.dumps({'id': id, 'version': '1.1',
                             'error': {'name': 'JSONRPCError',
                                       'code': code,
                                       'message': message

class JSONRPCService:
    def __init__(self, method_map={}):
        self.method_map = method_map

    def add_method(self, name, method):
        self.method_map[name] = method

    def __call__(self, request, extra=None):
        # We do not yet support GET requests, something pyjamas does
        # not use anyways.
        data = simplejson.loads(request.raw_post_data)
        # Altered to forward the request parameter when a member method
        # is invoked <>
        id, method, params = data["id"],data["method"],[request,]+data["params"]
        if method in self.method_map:
                result = self.method_map[method](*params)
                return response(id, result)
            except BaseException:
                etype, eval, etb = sys.exc_info()
                return error(id, 100, '%s: %s' %(etype.__name__, eval))
                etype, eval, etb = sys.exc_info()
                return error(id, 100, 'Exception %s: %s' %(etype, eval))
            return error(id, 100, 'method "%s" does not exist' % method)

def jsonremote(service):
    """Make JSONRPCService a decorator so that you can write :
    from jsonrpc import JSONRPCService
    chatservice = JSONRPCService()

    def login(request, user_name):
    def remotify(func):
        if isinstance(service, JSONRPCService):
            service.add_method(func.__name__, func)
            emsg = 'Service "%s" not found' % service.__name__
            raise NotImplementedError, emsg
        return func
    return remotify

Back to Top