Personal tools
You are here: Home Documentation Tutorials Grok Poller Tutorial Models

Models

Creating the application data model.
This tutorial shows how to implement a simple polling application using Grok.
Page 4 of 14.

The base class for models in Grok applications is aptly called the Model, subclasses of it can persist in the Zope Object Database [1] (ZODB), and also adds some containment attributes to it, more on containment later. The models are used for handling the "display-independent logic of the application" [2] in Grok.

[1]ZODB on Wikipedia <http://en.wikipedia.org/wiki/Zope_Object_Database>
[2]Grok Tutorial: Models <http://grok.zope.org/doc/current/tutorial.html#models>

For this application, we will implement two separate models:

Poll
which contains a poll question and the actual options. We will save a label and a description for every separate option. The options are implemented as their own objects.
PollOption
saves the label and description mentioned above. We could save them as a tuple sequence, for example, in the Poll object; but this design is a bit easier to handle as we don't have named tuples in Python 2.x series. It also supports our tutorial in the form generation part.

To implement these objects, we will create a new module named poll.py inside src/poller/ directory; where all files and directories created in this tutorial must be made.

Let's start with the PollOption implementation as it's the simpler object of the two:

import grok

class PollOption(grok.Model):
    label = u''
    description = u''

As you can see, the PollOption class is a subclass of grok.Model, which makes it persistent, as mentioned above -- additionally, this object defines the label and the description as class attributes.

The Poll object contains the question and the options, but we also wish to implement some methods for interfacing with the options and voting. Add the following to poll.py:

class Poll(grok.Model):
    question = u''

    def __init__(self):
        self._options = ()
        self._responses = {}

    def get_response(self, option):
        return self._responses[option]

    def choose(self, option):
        self._responses[option] += 1
        self._p_changed = True

    def get_options(self):
        return self._options

    def set_options(self, options):
        self._options = options
        self._responses = {}
        for option in self._options:
            self._responses[option.label] = 0

    options = property(get_options, set_options)

The code for this class is a copy [3] from zope.app.form package documentation [4]. It implements the options attribute as a Python property -- when the options attribute is set, it creates a "private" dictionary, which maps the options to votes. get_response() and choose() methods acts as the interface for the dictionary.

[3]The class includes a single modification from the original, it implements the __init__() method to initialize _options and _responses to work with empty polls.
[4]zope.app.form documentation <http://pypi.python.org/pypi/zope.app.form>

This is all basic Python code, except for:

self._p_changed = True

which tells the ZODB that a mutable attribute has changed in this object, which it doesn't know otherwise. ZODB saves all object attribute reference modifications, which means that it works for all immutable attributes automatically. Basically you can't use it directly with mutable objects such as Python list or dict, because modifying them won't change the attribute reference, and thus ZODB won't save them. ZODB provides some utilities for working with these basic types, we'll use one called PersistentDict here. By using PersistentDict in place of Python dict, ZODB automatically tracks changes to that dictionary; otherwise it works like dict. In the following code listing, we have removed the manual self._p_changed usage, and replaced the dict instantiation with PersistentDict:

from persistent.dict import PersistentDict

class Poll(grok.Model):
    question = u''

    def __init__(self):
        self._options = ()
        self._responses = {}

    def get_response(self, option):
        return self._responses[option]

    def choose(self, option):
        self._responses[option] += 1

    def get_options(self):
        return self._options

    def set_options(self, options):
        self._options = options
        self._responses = PersistentDict()
        for option in self._options:
            self._responses[option.label] = 0

    options = property(get_options, set_options)

The class could have been a bit simpler, but this implementation works great in the future for explaining some Grok concepts. For more information about ZODB, read Andrew Kuchling's ZODB/ZEO Programming Guide [5].

[5]<http://wiki.zope.org/ZODB/Documentation/guide/index.html>