Wiki has MovedThe Pyjamas Wiki is now at http://pyjs.org/wiki Way 1Using pyjams with Django is very straightforward, this is how I did it. PyJamas code for the UI: #!python
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 JSONRPCExamle.py. In appropriate urls.py I added: #!python
('test-jsonrpc/', 'test_jsonrpc'),The view maps to an instance of my JSONRPCService class, lets first see how the view is created and initialized the appropriate view.py, I have the following: #!python
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: #!python
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 Another WayDobes 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: 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 False
I 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. |
use pimentech's libcommonDjango - copy available at http://lkcl.net/libcommonDjango.tgz
use of that class entirely takes care of the marshalling and unmarshalling (known in json terms as "serialization") and offers you the means to use a simple class.
example:
from django.pimentech.network import
eventservice = JSONRPCService()
@jsonremote(eventservice) def get_event_details(request, event_id):
the advantage of pimentech's libcommonDjango is that it includes a python installer (setup.py) so you can get started immediately.
I created a brief walkthrough on this subject using libcommonDjango:
http://gdwarner.blogspot.com/2008/10/brief-pyjamas-django-tutorial.html