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
id name city
edit
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.