Personal tools
You are here: Home Documentation Tutorials Adder: an adding machine sample application

Adder: an adding machine sample application

Note: Return to tutorial view.

The most basic Grok app that is still of some use: an adding machine with tape.

Installing a Grok Application

Learn how to install an existing Grok application, and
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.

This is the simplest Grok application that is of still some use, it is a simple adding machine that keeps track of previous totals.

Installation

If you want to checkout the source code and run the application locally you can fetch it using subversion:

svn co svn://svn.zope.org/repos/main/grokapps/Adder Adder

This application comes with two files, a simple bootstrap.py that will install a 'buildout' binary into your project and a buildout.cfg that describes the configuration of your Grok project. The only thing to note at the moment is that this buildout.cfg sets a username and password of 'grok' and 'grok' with Site Manager access. You can install the application with the following commands:

$ cd Adder
$ python bootstrap.py
$ ./bin/buildout
NOTE::
copy versions.cfg from a newly created project (e.g. Sample) issue on MS Windows?

You'll get lots of output at this point and it will take some time to download and install of the necessary software. Later on you may wish to configure your own buildout defaults so that most parts are installed into a shared location and you can then buildout new applications very quickly.

You should now be able to start up an HTTP server to run your application. We'll can attach this process to the controlling terminal using the 'fg' (foreground) command. This way we can easily stop the server by pressing Ctrl-C, and any error messages will be sent to the terminal, making it easier to debug:

$ ./bin/zopectl fg

You should see text similar to the following, letting you know that the server is ready and listening on port 8080:

------
2008-01-06T16:35:02 INFO root -- HTTP:localhost:8080 Server started.
    Hostname: localhost
    Port: 8080
------
2008-01-06T16:35:02 INFO root Startup time: 2.760 sec real, 2.720 sec CPU

You can now point your web browser to http://localhost:8080/ and you will see the Grok Administration Interface. You can see that you currently have no installated applications, but you do have an application at adder.app.Adder available for installation. Create an instance of this application and name it adder, now you will see this show up in the installed applications section and this instance is available at the URL:

http://localhost:8080/adder/

When you install an application it will create the necessary bare-bones data required to run the application in your database. This application uses the ZODB, which is embedded as part of your application, and will store the data in a file at parts/data/Data.fs.

Installed Files

The layout for the installed files for the Adder application should look like this:

./installed-adder.png

The files in grey are stored in a source code management system such as subversion. The files in orange are downloaded from the internet when you run the ./bin/buildout command. In the screenshot above, the eggs directory is not present as this was installed elsewhere into a shared location. The bootstrap.py file is in red since this file is a required prerequisite to create ./bin/buildout that not all Grok applications may include a copy of it on the assumption that you already have a copy of this file on your system.

A Grok Application and a Grok Model

A Grok Application is something that can be installed. A Grok Model is an object that is saved in the Zope Object Database.

If you aren't following along with your own copy of the Adder application, you may find it helpful to refer to the main app.py file for the Adder application.

The convention in Grok is to put the code that you are developing for your application in a directory called src. If you look inside that directory you will see a package named adder. Inside that package you will see a file named app.py that contains the entire Adder application. Let's look at this file an see what it's doing.

import grok
from persistent.list import PersistentList

The grok package provides all of the core imports for creating a Grok application. The PersistentList class acts as an ordinary Python list that knows when it's been modified so that it can be transparently saved to the ZODB.

class Adder(grok.Application, grok.Model):
    """An adding machine with tape

    >>> adder = Adder()
    >>> adder.total
    0.0
    >>> adder.addTerm(0)
    0.0
    >>> '%.2f' % adder.addTerm(1.2)
    '1.20'
    >>> '%.2f' % adder.addTerm(-1)
    '0.20'

    Besides adding, Adder also contains a history of the added terms

    >>> ['%.2f' % term for term in adder.terms]
    ['0.00', '1.20', '-1.00']

    """

Every Grok application must have a single class that inherits from grok.Application. This will act as the root object for your application, depending on the size and scope of the application you are building you may use instances of this class to store things such as application specific settings and configuration. The Adder application also inherits from grok.Model, normally you will put your Models in seperate classes but since the Adder application is so simple, we can have a single class do double-duty as both an Application and a Model.

The docstring for the Adder class contains an example use of the class. You can try this code out for yourself on the Python interpreter. You will need to use a special version of the interpreter that has loaded Grok and Zope with your projects configuration, you can launch this with the zopectl command:

$ ./bin/zopectl debug
Welcome to the Zope 3 "debugger".
The application root object is available as the root variable.
A Zope debugger instance is available as the debugger (aka app) variable.

>>> from adder.app import Adder
>>> adder = Adder()

This example use code can also be programmatically fed into a Python interpreter and the expected output compared to the actual output. This form of automatically testing your application is called doctesting and is a very simple, easy concise way of adding tests to your application.

Remember when you created a new instance of the Adder application using the Grok Administration interface? This action called the constructor method of this Adder class.

def __init__(self):
    super(Adder, self).__init__()
    self.message = None
    self.clear()

Because we are defining our own __init__ method that overrides the default one supplied by the grok.Application base class, we must call super() to perform the necessary actions in that class. We then store a message attribute on the instance of our Adder application, and call a clear() method to reset our adding machine to 0. We define the clear() method next.

def clear(self):
    self.terms = PersistentList()
    self.total = 0.0

Here we assign an attribute called terms to an instance of the PersistentList class. We are tying our data model to the ZODB, an embedable Python object database by inheriting from a Persistent class in the persistent package. This uses the normal Python pickle module to store Python objects on disk in a transactionally safe manner. However, because Python Lists and Dictionaries are mutable objects, there is no way for instances of the Adder class to know when attributes that contain these data strucutres have been modified. The easiest solution to this problem is to use special PersistentList and PersistentDict objects from the persistent package.

Our adding machine needs to know how to update itself. We define this as part of our application model. When you are creating a web application it is important to seperate the application logic from the view logic that deals with rendering and responding to HTTP requests. Your application logic should never contain anything such as HTML output or parsing user input, but should only concern itself with expressing the intent of your application.

def addTerm(self, term):
    self.terms.append(term)
    self.total += term
    return self.total

A Grok View and a Grok Template

By design, our Model is not capable of interacting directly a web browser. Components that can respond to requests, typically returning HTML, are called View components. Since mixing HTML inside Python can get very messy, a Template component may be associated with a View component.

We can create a View simply by inheriting from the grok.View base class.

class Index(grok.View):

There is a Grok convention that if you only have a single Model object in a module, then all Views will automatically be associated with that Model. It's possible to explicitly override this behaviour, and it's required to associate your View with a specific Model if you have more than one Model class in a module.

When an adding error happens we display a message. We'll define the default messsage to be displayed as None.

message = None

If your Grok View has a method with the special name of update() then this will be called before your HTML template is rendered. This can be used to respond to user input, interact with your Models, and set attributes on the View object so that they will be available in to your template.

def update(self, term=None):
    if self.request.has_key('bt_clear'):
        self.context.clear()
    elif term:
        try:
            term = float(term)
        except ValueError:
            self.message = "Invalid number."
        else:
            self.context.addTerm(term)

Grok will marshall key/value pairs in the HTTP Request and automatically supply them as arguments to the update() method.

A Grok Template

The default templating language in Grok is called the Zope Page Templates. These use a sytnax called the Template Attribute Language (TAL), to include templating instructions as XML attributes. This has the advantage of allowing you to write templates that are valid XHTML syntax, as well as allowing your templates to look very similar to the XHTML output that they produce. A disadvantage to TAL is that they can only work with XML content. Views can either use the render() method to output text directly from the View, or you can use one of the many other Python templating languages with Grok.

The Grok convention is to associate templates with the name of module that contains the View. In these case the module is named app.py, and the View class is named Index, so it will be automatically associated with a template in the app_templates directory named index.pt. This template looks like this.

<html>
  <head>
    <link rel="stylesheet" type="text/css" tal:attributes="href static/adder.css">
    <title>Grok Adder</title>
    <script type="text/javascript">
        window.onload = function() { document.forms[0].term.focus() };
    </script>
  </head>
  <body>
    <h1>Adder</h1>
    <span tal:condition="view/message" tal:content="view/message" />
    <form tal:attributes="action view/url">
      <input type="text" name="term" />
      <input type="submit" value="+" name="bt_add" />
      <hr />
      <input tal:condition="context/terms" type="submit" value="Clear" name="bt_clear">
    </form>
    <table tal:condition="context/terms">
     <tr>
       <th>Total</th>
       <th tal:content="context/total"></th>
     </tr>
     <tr tal:repeat="term context/terms" tal:attributes="class repeat/term/even">
       <td class="count" tal:content="repeat/term/number">11</td>
       <td tal:content="term">11</td>
     </tr>
    </table>
  </body>
</html>

You will also want to include some CSS style with your application. We do this with the following line.

<link rel="stylesheet" type="text/css" tal:attributes="href static/adder.css">

This will make the CSS file from static/adder.css available to add the necessary style to your application. You can also put static Javascript files and images in this directory.

That's it!

You've now explored a complete, working Grok application. You've looked at the simplest possible persistent Model, a single View and template.

Adder: app.py

Main source code for the Adder application.

The file app.py in the adder package contains all of the Python code for the Adder application.

import grok
from persistent.list import PersistentList

class Adder(grok.Application, grok.Model):
    """An adding machine with tape

    >>> adder = Adder()
    >>> adder.total
    0.0
    >>> adder.addTerm(0)
    0.0
    >>> '%.2f' % adder.addTerm(1.2)
    '1.20'
    >>> '%.2f' % adder.addTerm(-1)
    '0.20'

    Besides adding, Adder also contains a history of the added terms

    >>> ['%.2f' % term for term in adder.terms]
    ['0.00', '1.20', '-1.00']

    """
    def __init__(self):
        super(Adder, self).__init__()
        self.message = None
        self.clear()

    def clear(self):
        self.terms = PersistentList()
        self.total = 0.0

    def addTerm(self, term):
        self.terms.append(term)
        self.total += term
        return self.total

class Index(grok.View):
    message = None

    def update(self, term=None):
        if self.request.has_key('bt_clear'):
            self.context.clear()
        elif term:
            try:
                term = float(term)
            except ValueError:
                self.message = "Invalid number."
            else:
                self.context.addTerm(term)