Django AMF User Manual

Last Updated: 2008-12-11 (Thu) 23:01:59 JST

System Requirements

  • Python 2.5 or 2.6
    Django AMF is developed with Python 2.6.

Installation

  1. Download djangoamf-x.x.zip from the project page on Sourceforge.jp.
  2. Uncompress the zip file on an arbitrary directory.
  3. Execute setup.py in the directory using the command as follow:
    python setup.py install
    'amf' package is installed in PYTHON_HOME/Lib/site_packeages directory.

Settings

  1. Add 'amf.django.middleware.AMFMiddleware' class to MIDDLEARE_CLASSES tuple in settings.py.
    MIDDLEWARE_CLASSES = (
        'django.middleware.common.CommonMiddleware',
        'django.contrib.sessions.middleware.SessionMiddleware',
        'django.contrib.auth.middleware.AuthenticationMiddleware',
        'django.middleware.doc.XViewMiddleware',
        'amf.django.middleware.AMFMiddleware',
    )
  2. Add the following variables in setting.py.
    VariableValueDefault Value
    AMF_GATEWAY_PATHURL path representing the endpoint for remoteing service/gateway/
    AMF_CLASS_MAPPERRefer to Custom Class Mapping section.empty dict
    AMF_TIME_OFFSETIf DATE typed data does not match your local time, set this variable.None
    AMF_AUTH_FUNCRefer to Authentication section.None
    AMF_LOG_FILEFile path into which Django AMF outputs log messages. If not set, Django AMF uses a default logger of python's standard logging module, and logging settings below are ignored.None
    AMF_LOG_LEVELLog levelINFO
    AMF_LOG_FILE_ENCODINGEncoding of log fileutf8
    AMF_LOG_MODEFile mode. 'a' or 'w'a
AMF_GATEWAY_PATH = '/gateway/'

AMF_LOG_LEVEL = 'INFO'
AMF_LOG_FILE = '/path/to/log.txt'
AMF_LOG_FILE_ENCODING = 'utf8'
AMF_LOG_FILE_MODE = 'a'

Data Types

Data types are automatically converted between Flash/Flex and Django AMF.

Flash/Flex -> Python

AMF3

Flash/FlexPython
int/uintint
Numberfloat
Stringstr
Booleanbool
Arraylist
Objectdict
Datedatetime.datetime
XMLElement (in xml.etree.ElementTree module)
ByteArrayamf.ByteArray
NullNone

AMF0

Flash/FlexPython
Numberfloat
Stringstr
Booleanbool
Arraylist
Objectdict
Datedatetime.datetime
XMLElement (in xml.etree.ElementTree module)
NullNone

Python -> Flash/Flex

AMF3

PythonFlash/Flex
intint
longNumber
floatNumber
decimal.DecimalNumber
basestring (str, unicode)String
boolBoolean
listArray
tupleArray
setArray
frozensetArray
django.db.models.query.QuerySetArray
dictObject
datetime.datetimeDate
datetime.dateDate
Element (in xml.etree.ElementTree module)XML
amf.ByteArrayByteArray
NoneNull

AMF0

PythonFlash/Flex
intNumber
longNumber
floatNumber
decimal.DecimalNumber
basestring (str, unicode)String
boolBoolean
listArray
tupleArray
sets.SetArray
setArray
django.db.models.query.QuerySetArray
dictObject
datetime.datetimeDate
Element (in xml.etree.ElementTree module)XML
NoneNull

Types other than listed above are treated as custom classes. For details, refer to Custom Class Mapping section.

QuerySet and its subclass object returned by Django Database API is treated as python list type.

Django AMF does not support RecordSet.

ByteArray type

Because Python deal with byte array data as string, Django AMF provides amf.ByteArray class which wraps ByteArray typed data sent from Flash/Flex application.
amf.ByteArray class has one attribute named 'data'. It holds byte array data.

Example

import amf

def saveByteArrayData(request, bytearray):
    """
    Saves to a file the ByteArray data sent from Flash/Flex.
    """
    data = bytearray.data
    f = open('filename', 'wb')
    f.write(data)
    f.close

def getByteArrayData(request):
    """
    Returns the ByteArray data of a file.
    """
    f = open('filename', 'rb')
    data = f.read()
    f.close()
    return amf.ByteArray(data)

View implementation for remoting service

Remoting service methods are implemented in a view module for Django. For Django view, refere to the page below:
http://www.djangoproject.com/documentation/tutorial3/

As view functions for HTML response, the first argument of a view function is HTTPRequest object. HTTPRequest object is passed by Django automatically, so Flash/Flex application need not care about it.

Example

  • example/views.py
    def calculate(request, arg1, arg2): #1
        return arg1 + arg2
    
    def calculate2(request, obj):       #2
        obj['sum'] = obj['arg1'] + obj['arg2']
        return obj
    
    def getCurrentDatetime(request):    #3
        import datetime.datetime
        return datetime.now()
    
    def getAllCustomers(request):       #4
        from example.models import Customer
        return Customer.objects.all()

#1 'calculate' function specifies arg1, arg2 arguments. In the funciton, add arg1 value and arg2 value, then return the result value. (If the arguments are string types, string concatenation result is returned.)

#2 'calculate2' function specifies obj as a argument which is supposed to be dict type holding arg1 and arg2 key. Add these key values and put the result to obj['sum'], then return the object. Flash/Flex receives this return value as Object type object.

#3 'getCurrentDateTime' function does not need any argument. It returns the datetime object representing the current time. Flash/Flex receives Date type object from this remoting service method.

#4 'getAllCustomers' function returns QuerySet of Customer class objects. Flash/Flex retrieves the returned value as Array. If Customer class is mapped to an ActionScript class using Custom Class Mapping, the elements of the Array are the mapped ActionScript class objects. Otherwise, the elements are Object type objects.

View function for remoting service returns the object which Flash/Flex application wants to receive as a result value of the remoting method.

Expose Remoting Service

To expose a view module as remoting service, add to URLConf a url pattern following the rule below:

 (r'^{AMF_GATEWAY_PATH}/{Remoting Service Name}/(.*)', 'amf.django.views', {'views':'{View Module Name}'})

or

(r'^{AMF_GATEWAY_PATH}/{Remoting Service Name}/{Method Name}', '{View Function Name}')

{Remoting Service Name} and {Method Name} can be any value.

The first pattern is called 'Auto Method Mapping', which invokes the same name view function as remoting service method called by Flash/Flex application.
The second pattern is called 'Exact Method Mapping', which maps a remoting service method to a view function one by one.

Example

  • urls.py
    from django.conf.urls.defaults import *
    import amf.django
    
    urlpatterns = patterns('',
       (r'^gateway/calculateService/(.*)', 'amf.django.views', {'views':'example.views'}), #1
       (r'^gateway/timeService/whatTimeIsItNow', 'exampe.views.getCurrentDatetime'),       #2
    )

    #1 pattern uses Auto Method Mapping. Remoting service invocation like calculateService.calculate(2, 3) calls calculate(request, arg1, arg2) function in example.views.py.
    If method which is not defined in view module is invoked, exception is thrown.

    #2 pattern uses Excact Method Mapping. timeService.whatTimeIsItNow() method is invoked from Flash/Flex application, then Django AMF calls getCurrentDatetime(request) function and sends the returned value to Flash/Flex.

Remoting Service Invocation from Flash/Flex

Example of calling calculateService.calculate method from Flash CS3 using Strippers Remoting Classes.

This example uses Strippers Remoting Classes.

import jp.strippers.remoting.*;
import flash.net.Responder;

var gatewayUrl:String = "http://127.0.0.1/gateway/";
var serviceName:String = "calculateService";
var serviceFactory:ServiceFactory = ServiceFactory.getInstance(gatewayUrl);
var service:RemotingService = serviceFactory.getService(serviceName);
// service.connection.setCredentials("username", "password"); // If authentication is necessary
var pc:PendingCall = service.calculate(2, 3);
pc.responder = new Responder(handleResult, handleError);

function handleError(result:int):void {
  trace("Result is" + result);
}

function onFault(fault:Object):void {
  trace(fault.faultstring);
}

Example of calling calculateService.calculate method from Flash 8.

This example uses Flash Remoting Components.

import mx.remoting.*;
import mx.rpc.*;

var gatewayUrl:String = "http://127.0.0.1/gateway/";
var serviceName:String = "calculateService";
var service:Service = new Service(gatewayUrl, null, serviceName, null, null);
var pc:PendingCall = service.calculate(2, 3);
pc.responder = new RelayResponder(this, "handleResult", "handleError");

function handleResult(re:ResultEvent):Void {
  trace("Result is" + re.result);
}

function handleError(fe:FaultEvent):Void {
  trace(fe.fault.faultstring);
}

Example of calling calculateService.calculate method from Flex2 using S2Flex2-components

This example uses S2Flex2-components.

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
    xmlns:s2="http://www.seasar.org/s2flex2/mxml"
    layout="absolute"
    applicationComplete="doCalculate()">
  <mx:Script>
    <![CDATA[
      import mx.controls.Alert;
      import mx.rpc.events.ResultEvent;
      import mx.rpc.events.FaultEvent;
      import mx.rpc.AsyncToken;

      private function doCalculate():void {
        var token:AsyncToken = service.calculate(2, 3);
        token.resultHandler = handleResult;
        token.faultHandler = handleFault;
      }

      private function handleResult(re:ResultEvent):void {
        Alert.show("Result is " + re.result);
      }

      private function handleError(fe:FaultEvent):void {
        Alert.show(fe.fault.faultDetail);
      }
    ]]>
  </mx:Script>

  <s2:RemoteService 
    id="service"
    gatewayUrl="http://127.0.0.1/gateway/"
    destination="calculateService"
    useAMF0="false"
    result="event.token.resultHandler(event)"
    fault="event.token.faultHandler(event)"
    showBusyCursor="true" /> 

</mx:Application>

If you would like to use AMF3, 'useAMF0' attribute of <s2:RemoteService> has to be set 'false'.

Custom Class Mapping

Custom Class Mapping maps ActionScript class(AS class) to Python class each other, and converts the object sent from/to remoting serivce into the mapped class object automatically.

There are two ways of defining custom class mappings:

  • Specify python class name in Flash/Flex applicaiton
  • Make AMF_CLASS_MAPPER module

Each way is explained below. Here, suppose that AS class 'com.example.CustomerDto' is mapped to python class 'example.models.Customer'.

Specify python class name in Flash/Flex applicaiton

Before invoking remoting service method, execute the following code.

  • ActionScript 1.0 and 2.0
    Object.registerClass("Python class name", AS class);
    Object.registerClass("example.models.Customer", com.example.CustomerDto);

Make AMF_CLASS_MAPPER module

Execute Object.registerClass() or registerClassAlias() method as explained above. But you can set arbitrary name instead of "Python class name".

import flash.net.*;
registerClassAlias("customer", com.example.CustomerDto);

Then, define a function named 'amf_class_mappings' in a python module you like. The function must returns dict object holding mapping information: Key is the arbirary name set in Flash/Flex, Value is python class name.

For example, define the funciton in exmple.views module (example/views.py),

def amf_class_mappings():
    return { "customer" : "example.models.Customer" }

then add AMF_CLASS_MAPPER variable in settings.py.

AMF_CLASS_MAPPER = 'example.views'

If the mapped class information or the mapped class itself is not found, the sent object is treated as dict type in python and as Object type in Flash/Flex.

Session

If sessions enabled in Django, a session object in HTTPRequest can be used in view function.
http://www.djangoproject.com/documentation/sessions/

Authentication

There are two ways to implement authentication system.

  • Defining custom authentication function
  • Using Django's authentication system

Defining custom authentication function

First of all, permission names to be used for user authentication are required. As an example, this document uses two permissions, "user" and "admin".

  • example/views.py
    from amf.django import permission_required
    
    def view_entry(request, entry_id):
      # Returns something like a blog entry. Anyone can invoke this.
    
    @permission_required('user')
    def post_comment(request, comment):
      # Post a comment. This can be invoked only by logged-in users.
    
    @permission_required('admin')
    def delete_comment(request, comment_id):
      # Delete a comment. This can be invoked only by administrators.

This views.py has three functions, one which does not require any permission, one which the users holding "user" permission can invoke, and which the users holding "admin" permission can invoke.

@permission_required('permission')

Add the decorator above to the view functions which require user authentication.
If some permissions are accepted, you can define them as below:

@permission_required('permission1, permission2, ...')

Next, you need to make a function to determine users' permissions. You can make this function in an arbitrary module. In this document, add the function to example/views.py.

  • example/views.py
    def _get_user_permissions(request, username, password):
      if username == 'foo' and password == 'bar':
         return 'user'
      if username == 'admin' and password == 'administrator':
         return 'admin'
      return None

The function for authentication must have three arguments. The first one is Request object, the second one is username of the user, and the third one is password of the user.

The above example returns "user" permission if username is "foo" and password is "bar". Real applications will use a database to get users' permissions.
If a user has multiple permissions, return them in tuple or list object.
Return None if a user does not have any permission.

Lastly, define this function name as the authentication function in settings.py.

  • settings.py
    AMF_AUTH_FUNC = 'sample.views._get_user_permissions'

Then, the view functions with @permission_required decorator can be invoked only by the user who has the specified permission.
If the user who does not have the requried permission try to call the function, Django AMF returns FaultEvent to Flash/Flex.

Set username and password in ActionScript

If using S2Flex2-components in Flex2, use setCredentials method of RemoteService instance.

removetService.setCredentials('username', 'password');

example

private function doCalculate():void {
    service.setCredentials('foo', 'bar'); // Set username and password
    var token:AsyncToken = service.calculate(2, 3);
    token.resultHandler = handleResult;
    token.faultHandler = handleFault;
}

Using Django's authentication system

Django comes with a user authentication system. It can be integrated into Django AMF.
User authentication in Django -- http://docs.djangoproject.com/en/dev/topics/auth/#topics-auth

Refer to the document page above for Django's authentication system and its configurations.

Using Django's authentication system, you do not need to make the function for authentication.
Add @permission_required decoreator to the view functions as menthoned before, and define the variable and value below in settings.py:

  • settings.py
    AMF_AUTH_FUNC = 'amf.django.authenticate'

That's all for authentication.

To create custom permissions like "user" and "admin", refere to http://docs.djangoproject.com/en/dev/topics/auth/#id1


Last-modified: 2008-12-11 (Thu) 23:01:59 (425d)