My favorites | Sign in
Project Home Issues
Search
for
DjangoWithPyjamas  
Some suggestions on using pyjamas and django together
Howto, Django, Json
Updated Jun 27, 2010 by luke.lei...@gmail.com

Wiki has Moved

The Pyjamas Wiki is now at http://pyjs.org/wiki

Way 1

Using 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 Way

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:

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.

Comment by project member luke.lei...@gmail.com, Oct 10, 2008

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):

return "hello %d" % event_id

the advantage of pimentech's libcommonDjango is that it includes a python installer (setup.py) so you can get started immediately.

Comment by gdwar...@gmail.com, Oct 21, 2008

I created a brief walkthrough on this subject using libcommonDjango:

http://gdwarner.blogspot.com/2008/10/brief-pyjamas-django-tutorial.html


Sign in to add a comment
Powered by Google Project Hosting