Grok ORM with Storm
===================
:Author: Christian Klinger (goschtl)
:Version: unkown
|
| RDBMS/ORM and Zope/Grok doesn't fit together?
| With this tutorial I will show you how easy it is,
| to make a simple CRUD Application with Grok and Storm.
Prerequisities
--------------
First we setup our database. As a lightweight solution I use sqlite. So please
make sure that sqlite is installed in your system. I think sqlite should be
included in every *nix distribution. After installing sqlite we set up our
database for our little application.
Here are the commands for an initial setup of our database::
note: here we create the database in /tmp/contact.db
chrissi$ sqlite3 /tmp/contact.db
SQLite version 3.1.3
Enter ".help" for instructions
sqlite> CREATE TABLE Contacts (id INTEGER PRIMARY KEY AUTOINCREMENT, name VARCHAR(200), city VARCHAR(200));
sqlite> .exit
After setting up the database let's build a new project with grokproject.::
chrissi$ bin/grokproject contacts
Fill out the asked questions from grokproject. I'm sure you have done this
before a dozen times. So it should not be a problem.
After setting up our grokproject environment we have to install a
stormcontainer, which acts like a normal grok.Container. The main difference is
of course that stormcontainer gets its data from an RDBMS via the Storm ORM
API. Unfortunately I don't have a release on pypi so we have to install it
manually. Let's install the stromcontainer:
First change to our grokproject package contacts::
chrissi$ cd contacts/
In this directory we have to checkout the stromcontainer::
chrissi$ svn checkout http://stormcontainer.googlecode.com/svn/trunk/ stormcontainer
A stormcontainer/bootstrap.py
A stormcontainer/buildout.cfg
A stormcontainer/setup.py
A stormcontainer/src
A stormcontainer/src/stormcontainer
A stormcontainer/src/stormcontainer/tests
A stormcontainer/src/stormcontainer/tests/stormcontainer.txt
A stormcontainer/src/stormcontainer/tests/__init__.py
A stormcontainer/src/stormcontainer/tests/test_unittests.py
A stormcontainer/src/stormcontainer/tests/test_doctests.py
A stormcontainer/src/stormcontainer/__init__.py
A stormcontainer/src/stormcontainer/utils.py
A stormcontainer/src/stormcontainer/interfaces.py
A stormcontainer/src/stormcontainer/components.py
A stormcontainer/.installed.cfg
After checkout we have to install the package stormcontainer to our python's
site-packages directory. This works with the command::
python2.4 setup.py develop
OK congratulations. Now we can start to developing our contacts application.
Step by step
------------
Let's change back to our contacts directory to define the interface for our
contact application.::
../src/contacts/
I will walk through the package structure of contacts to give you an impression
of what is required to let Grok work with Storm.::
configure.zcml
We have to include the package storm.zope. And to configure a store which is
responsible for connecting the database. Take a look at the uri in the store,
this points to our contact database which we have created.::
interfaces.py
from zope.interface import Interface
from zope.schema import Int, TextLine
class IContact(Interface):
""" Interface for Contacts """
id = Int(title=u"id",
description=u"The id of our contact",
readonly=True)
name = TextLine(title=u"Name",
description=u"The Name of our contact",
required=True)
city = TextLine(title=u"City",
description=u"The City of our contact",
required=True)
It's a normal interface. No ORM related dependencies.::
contact.py
import grok
from storm.locals import *
from interfaces import IContact
class Contact(grok.Model, Storm):
grok.implements(IContact)
__storm_table__ = "Contacts" # This is the corresponding table in our DB
id = Int(primary=True) # Here we give our attributes an "Storm" Datatype
name = Unicode()
city = Unicode()
class Edit(grok.EditForm):
grok.context(Contact)
form_fields = grok.AutoFields(IContact)
There is also no big deal in our model. We have to add a __storm_table__
attribute to our grok.Model this attribute reflects the corresponding table in
our database. We have to give our class attributes storm datatypes.::
app.py
import grok
from interfaces import IContact
from stormcontainer import StormContainer
from contact import Contact
class contacts(StormContainer, grok.Application, grok.Container):
""" this application inherits from StormContainer too """
def __init__(self):
super(contacts, self).__init__()
self.setClassName('contacts.contact.Contact')
self.setStoreUtilityName('contact')
class Index(grok.View):
def getContacts(self):
""" Return all Contacts of our StormContainer.
We can use the normal container API (items, keys ...)"""
rc=[]
for obj in self.context.items():
d={'uid': obj[0], 'id': obj[1].id, 'city': obj[1].city, 'name': obj[1].name}
rc.append(d)
return rc
class CreateContact(grok.AddForm):
form_fields = grok.AutoFields(IContact)
@grok.action('Create')
def create(self, **kw):
context = self.context
c = Contact()
self.applyData(c, **kw)
context['id']= c
self.redirect(self.url(self.context))
Here are the greatest changes to a normal grok application. Our application has
to inherit from StormContainer, grok.Application and grok.Container. In the
__init__ method of our application we set up two import parameters of our
application::
self.setClassName('contacts.contact.Contact') --> This is our model. --> contact.py
self.setStoreUtilityName('contact') --> This is the store utility name. --> configure.zcml
The method getContacts use the normal container API to get the results out of
the database through the Storm API. The CreateContact grok.AddForm should also
be standard.::
app_templates/index.pt
Congratulations!
Add new Contact
No big deal here we display the results in a nice table.
Now we can start our application::
zopectl fg
Now place your browser to localhost:8080 add an contact app.
Sometimes i got this error after adding my app::
ValueError: database parameter must be string or APSW Connection object
Then we have to patch this file::
lib/python2.4/site-packages/storm-0.11-py2.4.egg/storm/databases/sqlite.py - on line 173
Just make self._filename a string::
raw_connection = sqlite.connect(str(self._filename),
Now play a bit with adding and editing an contact. You can always look with
sqlite in your contact database to see what happens.
Further information
-------------------
Visit the storm page to get more information about storm.