Using Storm with Pylons and Repoze.tm2
This example shows how to integrate Storm with Repoze.tm2 in a Pylons application. Repoze.tm2 is a transaction manager which uses Zope's transaction package.
The example project will just have a simple controller with two action. The first action will add a person to a sqldb database and the second action will list all persons in the database.
You need to have the following packages:
- transaction
- zope.interface
- repoze.tm2
Optional dependencies when using the following patch, otherwise required (Storm <= 0.12)
- zope.security
- zope.testing
Create the project using
$ paster create -t pylons RepozeStorm
As explained in this blog you need to add repoze.tm2 to the pipeline of your Pylons application.
You should add the following pipeline and rename the app:main section.
[app:repozestorm] use = egg:RepozeStorm full_stack = false storm.database.persons.uri = sqlite:///%(here)s/repozestorm/data/persons.db [pipeline:main] pipeline = egg:Paste#cgitb egg:Paste#httpexceptions egg:repoze.tm2#tm repozestorm
Next define the model for persons repozestorm/model/person.py.
Person will be stored in a sql table called persons. Create that table by using setup-app. But for that to work make repozestorm/websetup.py:
1 """Setup the RepozeStorm application"""
2 import logging
3
4 from paste.deploy import appconfig
5 from pylons import config
6
7 from storm.zope.zstorm import global_zstorm
8
9 from repozestorm.config.environment import load_environment
10
11 log = logging.getLogger(__name__)
12
13 def setup_config(command, filename, section, vars):
14 """Place any commands to setup repozestorm here"""
15 conf = appconfig('config:' + filename, name=section.split(':')[1])
16 load_environment(conf.global_conf, conf.local_conf)
17
18 store = global_zstorm.get('persondb', config['storm.database.persons.uri'])
19 try:
20 store.execute("create table persons "
21 "(id integer primary key, "
22 "firstname varchar, lastname varchar)")
23 store.commit()
24 finally:
25 store.close()
Create directory to hold the database and run setup-app.
$ mkdir -p repozestorm/data $ paster setup-app development.ini#repozestorm Running setup_config() from repozestorm.websetup
Create a controller for the application:
1 import logging
2
3 from pylons import config
4
5 from storm.zope.zstorm import global_zstorm
6
7 from borg.lib.base import *
8 from borg.model.person import Person
9
10 log = logging.getLogger(__name__)
11
12 class PersonController(BaseController):
13
14 def index(self):
15 # Return a rendered template
16 # return render('/some/template.mako')
17 # or, Return a response
18 return 'Hello World'
19
20 def new(self):
21 store = global_zstorm.get('persondb', config['storm.database.persons.uri'])
22 p = Person()
23 p.firstname = u"Boo"
24 p.lastname = u"Foo"
25 store.add(p)
26 return "Done"
27 #raise Exception('Haha')
28
29 def list(self):
30 store = global_zstorm.get('persondb', config['storm.database.persons.uri'])
31 c.persons = list(store.find(Person))
32 return render('/list_persons.mako')
global_zstorm contains all Storm stores by key. In this example the uri associated with that key is stored in development.ini.
Because the application run inside a Repoze.tm2 pipeline, there is no need to call store.commit(). Every request to a controller is a transaction. If an exception is raised the transaction is rolled back or otherwise committed.
The controller action list calls a mako template (repozestorm/templates/list_persons.mako):
# -*- coding: utf-8 -*- <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html> <head> <title>List Persons</title> </head> <body> <div class="content"> <h1 class="main">List of persons</h1> <ul id="persons"> % for person in c.persons: <li> ${person.id} ${person.firstname} ${person.lastname} </li> % endfor </ul> </div> </body> </html>
And finally run the example:
$ paster serve --reload development.ini Starting subprocess with file monitor Starting server in PID 1234. serving on 0.0.0.0:5000 view at http://127.0.0.1:5000
And show an initially empty list of persons by going to http://localhost:5000/person/list. Add a person on http://localhost:5000/person/new and check again on http://localhost:5000/person/list.