Personal tools
You are here: Home Documentation How-Tos Grok ORM with Storm

Grok ORM with Storm

This How-to applies to: Any version.
This How-to is intended for: Any audience.

This howto describes a simple CRUD application with Grok and Strom.

Might be outdated! This document hasn't been reviewed for Grok 1.0 and may be outdated. If you would like to review the document, please read this post.

Purpose

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

<configure xmlns="http://namespaces.zope.org/zope"
           xmlns:grok="http://namespaces.zope.org/grok">
  <include package="grok" />
  <grok:grok package="." />

  <!-- Include the storm.zope in our application -->
  <include package="storm.zope"/>
  <include package="storm.zope" file="meta.zcml"/>

  <!-- Here is our Store this is a utility which holds the connection to DB -->
  <store name="contact" uri="sqlite:/tmp/contact.db"/>

</configure>

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

<html>
<head>
</head>
<body>
  <h1>Congratulations!</h1>

  <a href="createcontact"> Add new Contact </a>

  <table>
   <tr>
    <th> id </th>
    <th> name </th>
    <th> city </th>
    <th>  </th>
   </tr>
   <tr tal:repeat="person view/getContacts">
     <td tal:content="person/id"></td>
     <td tal:content="person/name"></td>
     <td tal:content="person/city"></td>
     <td> <a href="#" tal:attributes="href string: ${person/uid}/edit"> edit </a></td>
   </tr>
  </table>

</body>
</html>

 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

Than 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

It didn't run

Posted by http://heavylu.myopenid.com/ at May 14, 2008 06:39 PM

Thanks for your tutorial. I am pretty new to zope, so I have found grok to be an excellent tool for implementing web applications. This howto seems to be really helpful but I had some trouble when I tried to follow it: I got stuck to your directions (using "sinadlab" instead of "contacts") but when I started the application (zopectl fg), it returned an error:

>>File "~GROK-SANDBOX/Sinadlab/src/sinadlab/app.py", line 3, in ? >> from stormcontainer import StormContainer >>zope.configuration.xmlconfig.ZopeXMLConfigurationError: File >>"~GROK-SANDBOX/Sinadlab/parts/app/site.zcml", line 4.0-4.30 >> ZopeXMLConfigurationError: File "~GROK-SANDBOX/Sinadlab/src/sinadlab/configure.zcml", line >>5.2-5.27 >> ImportError: No module named stormcontainer

I work on Ubuntu 7.10. If you know what's wrong I would be glad if you let me know.

nva.stormcontainer

Posted by Christian Klinger at May 19, 2008 01:57 AM
Hi,

there is a new version of the stormcontainer in cheeseshop called nva.stormcontainer. I think with these version it should work.

http://pypi.python.org/pypi/nva.stormcontainer/0.2

If you have further questions ping me on irc #grok -- goschtl

HTH Christian

deleting

Posted by Daric Vladimir at Nov 20, 2008 02:08 PM

Great & clear tutorial.

How can I delete a "contact" ?

got it ! \\0//

Posted by Daric Vladimir at Nov 21, 2008 08:34 AM

First call deletecontact method from index template: ..

Delete

in app.py add DeleteContact class: ..

class DeleteContact(grok.View): def render(self, uid): context = self.context del context[uid] self.redirect(self.url(self.context))

something to update for not confusing newbie

Posted by Singuan Iap at Aug 13, 2009 03:53 AM
1) For zopectl to be created in contacts/bin, "--zopectl" shall be put into grokproject,
   ie. "grokproject --zopectl contacts"
2) The patch to strom is not required any more. (with nva.stormcontainer-0.2)
3) After the "bin/zopectl fg" executed, open the browser to http://localhost:8080/.
For seeing the index page of "Contacts", a dummy "application" has to be created.
Then navigate to that application. Otherwise, there is no any "Contacts" will
appear on the administrative page.

Thanks for Info

Posted by Christian Klinger at Aug 13, 2009 04:18 AM
Hi iap;

first thanks for info; I try to update this tutorial if i found some time to finish megrok.storm, which is a replacement for this stormcontainer thing. The stormcontainer was a first contribution to the grok community. So it´s far away from being perfect.

If you are intrested you can find megrok.strom in my Sandbox of zope repos:
http://svn.zope.org/[…]/storm

Of course this needs some cleaning too.

Christian