Z3 ECM Enterprise Content Management for Zope3

And you thought Zope 3 wasn't fun

If you think that Zope 3, or even Zope in general, isn't fun, agile, developer-friendly, easily understandable or "Pythonic", enter Grok. Here's an excerpt from Grok's README file:

Grok uses the Component Architecture and builds on Zope 3 concepts like content objects (models), views, and adapters. Its simplicity lies in using convention over configuration and sensible defaults when wiring components together. That means neither a configuration language like ZCML nor a lot of repitition are needed to create a web application with grok.

For the last couple of months, Grok has been developed by a handful of Zope 3 core developers. Like Five and lxml, Grok was originally thought up by Martijn Faassen at the EuroPython 2006 sprint. After we gave some input to Martijn's early design notes, five of us met for a first Grok sprint in October. This past weekend I had the pleasure of hosting the second Grok sprint. A first release is only a few weeks away, with a 1.0 scheduled some time in the second quarter this year (after a third sprint).

Since there have been several blog articles mentioning Grok and its philosophy already, I'd like to answer a FAQ that I get from developers:

So what does Grok code look like?

Imagine a simple web application for managing a herd of mammoths. For the sake of simplicity, we only support adding a mammoth to the herd and searching for it afterwards (more functionality like listing all mammoths in the database or deleting mammoths can be added with few lines of code). Data persistence will be provided automatically and transparently by the ZODB. The searching will facilitate the Zope Catalog utility. All of this fits into one Python module:

"""
herd.py -- grok-based web application for managing mammoths
"""
import grok
from zope import schema, interface, component
from zope.app.intid import IntIds
from zope.app.intid.interfaces import IIntIds
from zope.app.catalog.catalog import Catalog
from zope.app.catalog.interfaces import ICatalog
from zope.app.catalog.field import FieldIndex

def setup_catalog(catalog):
    catalog['name'] = FieldIndex('name', IMammoth) # only index mammoths

class Herd(grok.Application, grok.Container):
    """Root application object and container for Mammoth objects.
    Also sets up a catalog with a field index for indexing mammoths."""

    grok.local_utility(IntIds, provides=IIntIds) # necessary for catalog
    grok.local_utility(Catalog, provides=ICatalog, setup=setup_catalog)


class HerdIndex(grok.View):
    """Default view for herd objects.  It lets you search for mammoths
    inside the herd and add new mammoths.

    Actual HTML rendering by herd_templates/herdindex.pt (which is
    automatically found according to the name of this Python module
    and the name of this view class)"""

    grok.context(Herd) # indicates which model the view is for
    grok.name('index') # URL name of the view

    def update(self):
        """This method is executed before the template is rendered.
        It allows us to perform operations such as processing request
        parameters or precomputing stuff that the template may need."""
        query = self.request.form.get('query')
        if not query:
            return
        catalog = component.getUtility(ICatalog)
        self.search_results = catalog.searchResults(name=(query, query))


class IMammoth(interface.Interface):
    """Data schema for mammoths"""

    name = schema.TextLine(
        title=u'Name',
        description=u'Name of the Mammoth',
        )

class Mammoth(grok.Model):
    grok.implements(IMammoth)

    def __init__(self, name):
        self.name = name # this attribute will be indexed by the FieldIndex


class AddMammoth(grok.AddForm):
    """Form that creates a new mammoth."""
    grok.context(Herd)

    form_fields = grok.Fields(id=schema.TextLine(title=u"URL name"))
    form_fields += grok.AutoFields(IMammoth) # deduce form fields from schema

    @grok.action('Add mammoth')
    def add(self, id, name):
        self.context[id] = Mammoth(name)
        self.redirect(self.url(self.context))

The HTML of the default view for herds is rendered by the herdindex.pt template. By putting it in the herd_templates directory, it will be picked up automatically and associated with the view class in herd.py according to the name of the template file and the name of the view class:

<html> <!-- herd_templates/herdindex.pt -->
<body tal:define="results view/search_results|nothing">

<div>
  <h3>Search for a mammoth:</h3>
  <form tal:attributes="action view/url">
    <input type="text" name="query" />
    <input type="submit" value="Search for mammoth" />
  </form>
</div>

<div tal:condition="results">
  <h3>Search results:</h3>
  <ul>
    <li tal:repeat="mammoth results">
      <a href=""
         tal:attributes="href python:view.url(mammoth)"
         tal:content="mammoth/name">
        mammoth name goes here
      </a>
    </li>
  </ul>
</div>

<p>
  <a href="" tal:attributes="href python:view.url('addmammoth')">
    Add a mammoth
  </a>
</p>

</body>
</html>

That's it! That's the whole application! The only teeny bit of ZCML that we need is for registering the application with Zope 3:

<grok:grok package="herd" />

This will tell Grok to "grok" the components we defined and register them accordingly. No ZCML or any other configuration needed. Just take it out of the box and have fun. (I should note though that currently, this example will require some ZCML so that the initial Herd container can be created. We hope to solve that problem by the first release or the 1.0)

And you thought Zope 3 wasn't fun? We're just warming up!

Update: After receiving lots of feedback, particularly about the before() method on grok views, we decided to rename before() to update() in an attempt to make the name less confusing and more similar to existing Zope 3 idioms. update() can now also issue a redirect which will cause the template not to be rendered subsequently. Thanks to Martin Aspeli for suggesting that. The code snippet above has been changed accordingly.

Update 2: Grok now has an Application base class for an application's root object, so I updated the Herd class to use that.

Update 3: Grok now has a web site at http://grok.zope.org!

Posted by Philipp von Weitershausen @ 01/09/2007 09:13 PM. - Categories: Python, Zope 3 -  0 comments