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