-
Notifications
You must be signed in to change notification settings - Fork 211
pyjamaswithdjangojsonrpc
Some suggestions on using Pyjamas and Django together.
Please note that the use of the jsonrpc.py file in the standard pyjamas distribution is, for django, much much simpler. (lkcl)
Using Pyjamas with Django is very straightforward, this is how I did it.
from ui import RootPanel, TextArea, Label, Button, HTML, VerticalPanel
from JSONService import JSONProxy
class DjangoTest:
def onModuleLoad(self):
self.TEXT_WAITING = "Waiting for response..."
self.TEXT_ERROR = "Server Error"
self.remote = EchoService()
self.status=Label()
self.text_area = TextArea()
self.button = Button("Send text to Echo Service", self)
self.button2 = Button("Send text to Echo Service 2", self)
panel = VerticalPanel()
panel.add(HTML("<b>JSON-RPC Example</b>"))
panel.add(self.text_area)
panel.add(self.button)
panel.add(self.button2)
panel.add(self.status)
RootPanel().add(panel)
def onClick(self, sender):
self.status.setText(self.TEXT_WAITING)
if sender == self.button:
id = self.remote.echo(self.text_area.getText(), self)
else:
id = self.remote.echo2(self.text_area.getText(), self)
if id<0:
self.status.setText(self.TEXT_ERROR)
def onRemoteResponse(self, response, request_info):
self.status.setText(response)
def onRemoteError(self, code, message, request_info):
self.status.setText("Server Error or Invalid Response: ERROR " + code + " - " + message)
class EchoService(JSONProxy):
def __init__(self):
JSONProxy.__init__(self, "/gtd/test-jsonrpc/", ["echo", "echo2"])This code is highly derived from JSONRPCExample.py.
In appropriate urls.py I added:
('test-jsonrpc/', 'test_jsonrpc'),The view maps to an instance of my JSONRPCService class, lets first see how the view is created and initialized [in the appropriate view.py, I have the following]:
test_jsonrpc = JSONRPCService()
def echo(d): return d.upper()
def echo2(d): return d.lower()
test_jsonrpc.add_method("echo", echo)
test_jsonrpc.add_method("echo2", echo2)Finally the JSONRPCService class I wrote:
from django.utils import simplejson
from django.http import HttpResponse
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):
#assert extra == None # we do not yet support GET requests, something pyjams do not use anyways.
data = simplejson.loads(request.raw_post_data)
id, method, params = data["id"], data["method"], data["params"]
if method in self.method_map:
result = self.method_map[method](*params)
return HttpResponse(simplejson.dumps({'id': id, 'result': result}))
else:
return HttpResponse(simplejson.dumps({'id': id, 'error': "No such method", 'code': -1}))The code here is licensed under BSD if you care to use. Happy pyjaming django!
-- Amit Upadhyay
Dobes Vandermeer:
Here's how I did it. I created json service wrapper class like so:
from django.db import models
from django.core.serializers import base
from django.db import models
from django.core.serializers.json import DateTimeAwareJSONEncoder
import sys
from traceback import print_exc
from django.utils.simplejson.decoder import JSONDecoder
from django.http import HttpResponse
def jsonmethod(f):
"""
A simple proxy used to indicate that the given method is in fact
available to json clients.
"""
f.json_public = True
return f
class MySerializer(base.Serializer):
"""
Serializes a QuerySet to basic Python objects.
"""
def start_serialization(self):
self._current = None
self.objects = []
def end_serialization(self):
pass
def start_object(self, obj):
self._current = {
"model" : str(obj._meta),
"pk" : str(obj._get_pk_val()),
}
def end_object(self, obj):
self.objects.append(self._current)
self._current = None
def handle_field(self, obj, field):
if isinstance(field, models.FileField) or isinstance(field, models.DateTimeField):
self._current[field.name] = self.get_string_value(obj, field)
else:
self._current[field.name] = getattr(obj, field.name)
def handle_fk_field(self, obj, field):
related = getattr(obj, field.name)
if related is not None:
related = related._get_pk_val()
self._current[field.name] = related
def handle_m2m_field(self, obj, field):
self._current[field.name] = [related._get_pk_val() for related in getattr(obj, field.name).iterator()]
def getvalue(self):
return self.objects[0]
class DjangoAwareJSONEncoder(DateTimeAwareJSONEncoder):
def default(self, o):
if isinstance(o, models.Model):
return MySerializer().serialize([o])
else:
return super(DjangoAwareJSONEncoder, self).default(o)
class JSONService(object):
"""
Wrapper for a view func to indicate that it should de-serialize any
POST json inputs and serialize the return value into a json result.
Be sure to define a getMethodImplementation() or a put @jsonmethod
in front of each public method.
"""
class Error(Exception):
"""
Raise this exception type to return an error to the client from
an rpc handling method
"""
def __init__(self, code=0, message='error'):
self.code = code
self.message = message
def asDict(self):
return {"code":self.code, "message":self.message}
def getMethodImplementation(self, name):
"""
Get the implementation of a method by method name._
"""
try:
f = getattr(self, name)
except AttributeError:
return None
try:
if f.im_func.json_public:
return f
except AttributeError:
pass
try:
if f.json_public:
return f
except AttributeError:
pass
return None
def getAvailableMethods(self):
return self.__class__.__json_rpc_methods__
def makeResponse(self, id, result, code=0, error=None):
if error or code: error = {"code":code, "message":error}
return {"result":result, "id":id, "error":error}
def __call__(self, request, *args, **kwargs):
if len(request.POST):
rpcRequest = JSONDecoder().decode(request.raw_post_data)
else:
rpcRequest = dict(request.GET)
try: rpcRequest['params'] = JSONDecoder().decode(request['params'])
except KeyError: pass
#print 'attempting call ... ', rpcRequest
id = None
params = None
errorCode = 0
try:
method = rpcRequest["method"]
except KeyError:
result = None
error = "Invalid JSON-RPC request; method must be specified: %r"%(rpcRequest) # Not a JSON request
else:
try: params = rpcRequest["params"]
except KeyError: params = None
try: id = rpcRequest["id"]
except KeyError: id = None
try: mode = request["mode"]
except KeyError: mode = "json"
try:
f = self.getMethodImplementation(method)
if f:
if params is not None: args = list(args).extend(params)
try: func = f.im_func
except AttributeError: func = f
argnames = func.func_code.co_varnames[len(params):func.func_code.co_argcount]
for var in ("request", "rpcRequest", "id", "method"):
if var in argnames:
kwargs[var] = locals()[var]
result = f(*params, **kwargs)
error = None
else:
result = None
error = "No such method %r; available methods are %r"%(method, self.getAvailableMethods())
except JSONService.Error, x:
errorCode = x.code
error = x.message
result = None
except:
# stackTrace = StringIO()
print 'exception raised'
sys.stdout.flush()
print_exc()
sys.stderr.flush()
raise
# error = stackTrace.getvalue()
# result = None
response = self.makeResponse(id, result, errorCode, error)
#print 'response', response
json = DjangoAwareJSONEncoder(ensure_ascii=False).iterencode(response)
if mode == "json":
return HttpResponse(json, mimetype='application/json')
elif mode == "text":
return HttpResponse(json, mimetype='text/plain')Instead of using "views.py" in the app, I create a file service.py:
BUG AM 2010-08-05 It has been pointed out that the login() method is recursive and unlikely to work. Patches welcome to pyjamasdev@pyjs.org
from habitsoft.login.models import Invitation
from habitsoft.login.models import EmailChange
from datetime import datetime
import socket
from django.conf import settings
from django.core.mail import send_mail
from django.template.context import Context
from django.template.loader import get_template
from django.core.exceptions import ObjectDoesNotExist
from habitsoft.login.models import Account
from random import getrandbits
from django.contrib.auth import logout
from django.contrib.auth import login
from django.contrib.auth import authenticate
from habitsoft.webframework.json import JSONService, jsonmethod
class LoginService(JSONService):
def __init__(self):
pass
@jsonmethod
def login(self, username, password, request):
user = authenticate(username=username, password=password)
if user is not None:
login(request, user)
return True
else:
return False
@jsonmethod
def logout(self, request):
logout(request)
@jsonmethod
def getProfile(self, request):
account = request.user
return {"first_name":account.first_name,
"last_name":account.last_name,
"country_code":account.country_code}
@jsonmethod
def updateProfile(self, first, last, country_code, request):
account = request.user
account.first_name = first
account.last_name = last
account.country_code = country_code
account.save()
@jsonmethod
def changePassword(self, oldPassword, newPassword, request):
account = request.user
if account.check_password(oldPassword):
account.set_password(newPassword)
account.save()
return True
return FalseI created another file in the project, next to settings.py, called services.py like this:
from mysite.login.service import LoginService
from django.conf.urls.defaults import *
login = LoginService()
services = ('login',)
urlpatterns = patterns('habitsoft.services',
*[('^%s/$'%name, name) for name in services]
)In urls.py:
from django.conf.urls.defaults import patterns
from django.conf.urls.defaults import include
urlpatterns = patterns('',
# ...
(r'^services/', include('habitsoft.services')),
# ...
)Sample usage in a pyjamas file:
class RemoteLoginService(JSONProxy):
def __init__(self):
JSONProxy.__init__(self, "services/login/",
["login",
"logout",
"signUp",
"activate",
"getProfile",
"updateProfile",
"changePassword",
"changeEmail",
"confirmEmail"])
login = RemoteLoginService()
class JsonHelloButtonHandler:
def __init__(self, textArea):
self.textArea = textArea
def onClick(self, sender):
self.textArea.setHTML("sending request...")
login.login("user", "pass")
def onRemoteResponse(self, response, request_info):
self.textArea.setHTML("response: "+response)
def onRemoteError(self, code, message, request_info):
self.textArea.setHTML("error: "+message)
class FrontPage:
def __init__(self):
self.testText = "test"
def greet(self, sender):
Window.alert("Hello, AJAX! self = "+self+" prototype = "+self.prototype)
def onModuleLoad(self):
root = RootPanel()
root.add(Button("Alert", self.greet))
self.textArea = HTML()
root.add(Button("JSON", JsonHelloButtonHandler(self.textArea)))
root.add(self.textArea)
self.textArea.setHTML("This is a test.")
def toString(self):
return "FrontPage instance"Feel free to use this for any purpose.
- Pyjamas + Django = Pure Win (hands-on introduction by Derek Schaefer)