Latest Version: 0.9.6.2
  Dashboard > Pylons Cookbook > ... > Controllers > SOAP Controller with Optio's Soaplib and SQLAlchemy
  Pylons Cookbook Log In | Sign Up   View a printable version of the current page.  
  SOAP Controller with Optio's Soaplib and SQLAlchemy
Added by Lysander David, last edited by Lysander David on Mar 19, 2008  (view change) show comment
Labels: 

Name Space Section Page Version Status Reviewed Author(s)
SOAP Controller with Optio's Soaplib and SQLAlchemy Pylons Cookbook Controllers SOAP Controller with Optio's Soaplib and SQLAlchemy 1.0 Draft False Lysander David

Introduction

This recipe goes over using Soaplib ( http://trac.optio.webfactional.com/wiki/soaplib )
and SQLAlchemy ( http://www.sqlalchemy.org/ ) to expose a
SOAP web service.

Install soaplib, sqlalchemy and sqlite

You will need to install Routes 1.7 version for soaplib to function properly behind Pylons.

1
2
3
sudo easy_install -U Routes
sudo easy_install soaplib
sudo easy_install sqlalchemy

It is only necessary to install sqlite if you are using a version of python < 2.5

1
sudo easy_install sqlite

Soaplib and sqlalchemy ( and possibly sqlite ) should now be installed and ready to use.

Create a pylons application named user_manager_soap

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
mkdir ~/explore/python/pylons/02_user_manager_soap
paster create -t pylons
Selected and implied templates:
  Pylons#pylons  Pylons application template

Enter project name: user_manager_soap
Variables:
  egg:      user_manager_soap
  package:  user_manager_soap
  project:  user_manager_soap
Enter version (Version (like 0.1)) ['0.1']: 
Enter sqlalchemy (True/False: Include SQLAlchemy 0.4 configuration) [False]: True
Enter template_engine (mako/genshi/etc: Template language) ['mako']: 
Enter zip_safe (True/False: if the package can be distributed as a .zip file) [False]: 
Creating template pylons

.
.
.

Lots of stuff

Create a SOAP Controller

Execute this command to create the controller.

1
paster controller user_manager

This will create a file named user_manager_soap/controllers/user_manager.py

Modify the contents of the file to match:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
import logging

from pylons import request, response, session
from pylons import tmpl_context as c
from pylons.controllers.util import abort, redirect_to, url_for

from user_manager_soap.lib.base import BaseController

from soaplib.wsgi_soap import SimpleWSGISoapApp
from soaplib.service import soapmethod
from soaplib.serializers.primitive import String, Integer, Array
from soaplib.serializers.clazz import ClassSerializer

from user_manager_soap.model.meta import Session
from user_manager_soap.model.meta import engine
import user_manager_soap.model as model

from user_manager_soap.model.permissions import Permission
from user_manager_soap.model.users import User

log = logging.getLogger(__name__)

class UserManagerService(SimpleWSGISoapApp):

    @soapmethod(User,_returns=User)
    def add_user(self,user):
        Session.save(user)
        Session.commit()
        return user

    @soapmethod(Integer,_returns=User)
    def get_user(self,user_id):
        return Session.query(User).get(user_id)

    @soapmethod(User)
    def modify_user(self,user):
        update_user = Session.query(User).get(user.id)
        sync_to_update(user,update_user)
        Session.update(update_user)
        Session.commit()
        return user

    @soapmethod(Integer)
    def delete_user(self,user_id):
        delete_user = Session.query(User).get(user_id)
        Session.delete(delete_user)
        Session.commit()

    @soapmethod(_returns=Array(User))
    def list_users(self):
        return Session.query(User).all()

UserManagerController = UserManagerService()

Set up models

Create a model file for the permissions table named
user_manager_soap/model/permissions.py with the following contents.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
from soaplib.serializers.primitive import String, Integer, Array
from soaplib.serializers.clazz import ClassSerializer

class Permission(ClassSerializer):
    class types:
        id = Integer
        application = String
        feature = String

    def __init__(
        self,
        id=None,
        application=None,
        feature=None
        ):
        self.id = id
        self.application = application
        self.feature = feature

Create a model file for the users table named
user_manager_soap/model/users.py

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
from soaplib.serializers.primitive import String, Integer, Array
from soaplib.serializers.clazz import ClassSerializer
from permissions import Permission

class User(ClassSerializer):

    class types:
        id = Integer
        username = String
        firstname = String
        lastname = String
        permissions = Array(Permission)

    def __init__(
        self,
        id=None,
        username=None,
        firstname=None,
        lastname=None,
        permissions=None
        ):
        self.id = id
        self.username = username
        self.firstname = firstname
        self.lastname = lastname
        self.permissions = permissions or []

Modify user_manager_soap/model/_init_.py to match this

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
import sqlalchemy as sa
from sqlalchemy import orm

from user_manager_soap.model import meta
from user_manager_soap.model import users
from user_manager_soap.model import permissions

def init_model(engine):
    """Call me before using any of the tables or classes in the model."""
    sm = orm.sessionmaker(autoflush=True, transactional=True, bind=engine)

    meta.engine = engine
    meta.metadata = sa.MetaData(engine)
    meta.Session = orm.scoped_session(sm)

    permissions.permissions_table = sa.Table(
        'permissions',
        meta.metadata,
        sa.Column('id', sa.types.Integer, primary_key=True),
        sa.Column('application', sa.types.Unicode(255), nullable=False),
        sa.Column('feature', sa.types.Unicode(255), nullable=False),
        sa.Column('user_id', sa.types.Integer, sa.ForeignKey('users.id')),

    )

    users.users_table = sa.Table(
        'users',
        meta.metadata,
        sa.Column('id', sa.types.Integer, primary_key=True),
        sa.Column('username', sa.types.String(255), nullable=False),
        sa.Column('firstname', sa.types.String(255), nullable=False),
        sa.Column('lastname', sa.types.String(255), nullable=False)
    )

    orm.mapper(
        users.User,
        users.users_table,
        properties={
            'permissions': orm.relation(
                permissions.Permission,
                lazy=False,  #  you want to fetch all addresses
                             # when you fetch the User
                backref="user",
                cascade="save-update"
                )
            }
        )

    orm.mapper(permissions.Permission, permissions.permissions_table)

    meta.metadata.create_all(checkfirst=True)

Modify Routes To Download WSDL

Insert the following line

1
map.connect(':(controller).wsdl')

before

1
map.connect(':controller/:action/:id')

in config/routing.py so that make_map looks like

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
def make_map():
    """Create, configure and return the routes Mapper"""
    map = Mapper(directory=config['pylons.paths']['controllers'],
                 always_scan=config['debug'])

    # The ErrorController route (handles 404/500 error pages); it should
    # likely stay at the top, ensuring it can always be resolved
    map.connect('error/:action/:id', controller='error')

    # CUSTOM ROUTES HERE
    map.connect(':(controller).wsdl')
    map.connect(':controller/:action/:id')

    return map

This will allow the wsdl to be downloaded using the url
http://localhost:5000/user_manager.wsdl

Test the SOAP service

Start the server with

1
paster serve --reload development.ini

Open a python prompt in your projects directory, and run the following code:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
python
Python 2.5.1 (r251:54863, May  8 2007, 18:17:38) 
[GCC 4.1.2 (Ubuntu 4.1.2-0ubuntu4)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> from soaplib.client import make_service_client
>>> from user_manager_soap.controllers.user_manager import UserManagerService
>>> client = make_service_client('http://localhost:5000/user_manager',UserManagerService())
>>> from user_manager_soap.model.permissions import Permission
>>> from user_manager_soap.model.users import User
>>> bob_user = User()
>>> bob_user.username = 'buser'
>>> bob_user.firstname = 'Bob'
>>> bob_user.lastname = 'User'
>>> bob_user.permissions = [ Permission(application='admin', feature='create')]
>>> ret_user = client.add_user(bob_user)
>>> ret_user.__dict__
{'username': 'buser', 'lastname': 'User', 'id': 1, 'firstname': 'Bob', 'permissions': [<user_manager_soap.model.permissions.Permission object at 0x8b1440c>]}
>>> ret_user = client.get_user(1)
>>> ret_user.__dict__
{'username': 'buser', 'lastname': 'User', 'id': 1, 'firstname': 'Bob', 'permissions': [<user_manager_soap.model.permissions.Permission object at 0x8b1096c>]}
>>> ret_user.username = 'notbuser'
>>>

Open sqlite3 in another window and type this to verify the current state of the database

1
2
3
4
5
6
qlite3 development.db
SQLite version 3.3.17
Enter ".help" for instructions
sqlite> select * from users;
1|buser|Bob|User
sqlite> .q

In the python prompt modify a user

1
>>> client.modify_user(ret_user)

In the sqlite window, type this to verify that the user has
been updated

1
2
3
4
5
6
sqlite3 development.db
SQLite version 3.3.17
Enter ".help" for instructions
sqlite> select * from users;
1|notbuser|Bob|User
sqlite> .q

In the python prompt delete a user

1
>>> client.modify_user(ret_user)

In the sqlite window, type this to verify that the user has
been deleted

1
2
3
4
5
sqlite3 development.db
SQLite version 3.3.17
Enter ".help" for instructions
sqlite> select * from users;
sqlite> .q

In the python prompt add several users then list the users
to verify that list_users works

1
2
3
4
5
6
7
8
>>> client.add_user(bob_user)
<user_manager_soap.model.users.User object at 0x8b10d0c>
>>> client.add_user(bob_user)
<user_manager_soap.model.users.User object at 0x8b10a0c>
>>> client.add_user(bob_user)
<user_manager_soap.model.users.User object at 0x8b10c2c>
>>> [ ( user.id, user.username ) for user in client.list_users() ]
[(1, 'buser'), (2, 'buser'), (3, 'buser')]

Sample Code

The sample can be downloaded here user_manager_soap.tar.gz