by Brandon Rhodes • Home

My November Grok Presentation

Date: 9 November 2007
Tags:computing, grok, python, zope

In this post, I provide the slides and examples from a recent talk that I gave to some fellow software developers at Georgia Tech. Many of them were not familiar with web frameworks, and I wanted to introduce them to two common concepts: the idea of “convention over configuration,” and the practice of passing inert data structures to a page template rather than letting it access live objects directly.

But because I am also really enjoying my work with the new Python web framework Grok, I decided to make it the centerpiece of my presentation and illustrate the more general principles of web development by showing how Grok works. I also gave the talk a week later at an Atlanta Plone gathering, where I knew the interest would be more on what makes Grok unique.

When I was asked for a copy of my slides, I realized that my presentation style — which involves showing several slides, moving to my editor and web browser for some actual programming, then diving back into my slides — leaves the PDF of the presentation perhaps not making as much sense by itself as it might otherwise:

Click here to download the PDF of my presentation

Hence, this post. Here I take the reader through the aspects of my talk that failed to make it on to the slides, showing both the command-line operations and the coding that illustrated and made sense of the presentation itself.

Creating a Grok Instance

After some initial slides introducing the idea of “convention over configuration,” my presentation introduces Grok itself — making good use, I think, of the symbolic value of his club — and then announces, “Let's create a Grok instance!” This is the point at which I bring up a command prompt and show the audience how easy grokproject makes it to start a new project:

$ grokproject MyApp
$ ./MyApp/bin/zopectl fg

After zopectl has finished starting up, I point my web browser at http://localhost:8080/, and then suffer a silent moment of frustration about the fact the web page that appears is not simply the welcome message from the new Grok app. Instead, the “admin interface” comes up, and insists on being fed an application instance name before allowing me to proceed. I choose lotr as the name, then show the audience that http://localhost:8080/lotr is where the new application lives.

Before continuing with my slides, I visit the directory MyApp/src/MyApp and give the audience brief glimpses of both app.py and the app_templates directory. This introduces three items which will be critical as the slides resume: the MyApp object, the Index view, and the index.pt page template. With these three items pointed out, I am ready to proceed with my slides and discuss how the object, view, and page template together form Grok's “Threefold Way” of making objects available on the web.

The Actual Example

Only a few slides later, the time arrives for a more sophisticated example. The presentation exaggerates when it instructs me to “go add some models and further views to your application.” I do not, in fact, make the audience watch me write code, but present them with a finished and working example that I prepare before the presentation. You can create it for yourself by putting this file in place of the one grokproject created:

and then putting these four files in app_templates:

The resulting application, though tiny, was probably too complex for my target audience — many eyes, I noticed, glazed over when seeing Zope Page Templates for the first time — but four views is really barely enough to illustrate both that you can have multiple views on the same object (MyApp has both an /index and a /contents), and that several objects can have views with the same name (Battle and Character have different /index views). The example also shows clearly that if you declare four views, you need to write four page templates to go with them.

I emphasize for the audience that most of the 56 lines of app.py are concerned with creating the violence-themed object model; only the fifteen lines at the bottom are interesting Grok code, creating four very slender views. This is where I point out “convention over configuration” at work: all of the page templates get discovered without having to be named, and grok.name() only has to be employed when we want the view's URL name to differ from the name of the class.

After demonstrating that the application does, in fact, work, we return to the slides and start examining the relationship between page templates and their views.

Moving to a “push” model

In the presentation I distinguish between “muscular views” and “muscular templates”; perhaps I should have used instead the more conventional idea of templates that “pull” data versus those to which data are “pushed.”

When I reach the slide that reminds me to, “show them your anemic View classes,” I return to my editor and point out that, in the example application, each view has almost no purpose but to associate a particular object class with a particular page template. There is very little information in a declaration like:

class CharacterIndex(grok.View):
    grok.context(Character)
    grok.name('index')

Whereas there is plenty going on in a template like characterindex.pt; every TAL reference it makes is either to the raw context object itself, or an object it reaches through the context! Take a look:

<h1>The Character:
<span tal:content="context/name">Name</span></h1>
This character fought in several battles.
<ul>
  <li tal:repeat="battle context/battles">
    <a tal:attributes="href python: view.url(battle)"
       tal:content="battle/name">Name</a>
  </li>
</ul>

The slides then continue, pointing out that this makes for very quick coding at the cost of several disadvantages. When the fourth and final intermission arrives (“go make your CharacterIndex View more muscular”), I return to the window where my editor has app.py open and rewrite the CharacterIndex to look more like this:

class CharacterIndex(grok.View):
    grok.context(Character)
    grok.name('index')
    def namespace(self):
        return {
            'character_name': self.context.name,
            'battles': [
                { 'name': battle.name,
                   'url': self.url(battle) }
                for battle in self.context.battles
                ],
            }

Note what this does: it takes all of the work that the page template was doing in order to fetch the character's name, and the names and URLs of the battles in which the character fought, and performs that access in pure Python and in exactly once place. The values in the dictionary that is being passed — character_name and battle_info — are, if you will examine them carefully, composed of nothing but primitive Python types! They consist solely of lists and dictionaries containing strings. They contain no objects for the template to browse at its leisure; instead, every bit of pertinent data in our objects is reduced to harmless data structures which cannot be used to affect the original objects in any way. This means that the characterindex.pt template can be rewritten both more safely, and also more simply:

<html><body>
  <h1>The Character:
  <span tal:content="character_name">Name</span></h1>
  This character fought in several battles.
  <ul>
    <li tal:repeat="battle battles">
      <a tal:attributes="href battle/url"
         tal:content="battle/name">Name</a>
    </li>
  </ul>
  <a href="index">Return home</a>
</body></html>

And after showing the audience that the rewritten view and the rewritten page template work together, I return to my slides and — finally! — conclude my presentation.

The presentation seemed to weigh in at around forty minutes. Next time, I think I will tackle fewer concepts at once. But I do like making lots of use of the picture of the cave man, and his club.

©2021