| Name | SOAP Controller with Optio's Soaplib and SQLAlchemy |
|---|---|
| Space | Pylons Cookbook |
| Section | Controllers |
| Page | SOAP Controller with Optio's Soaplib and SQLAlchemy |
| Version | 1.0 |
| Status | Draft |
| Reviewed | False |
| Author(s) | 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