Log in |
Introduction to Zope 2.6: The New Zope ZooThis tutorial was originally written to be the "Creating Basic Zope Applications" chapter of version 2.6 of the Zope Book. Since it has never appeared there to date, I present it here. This tutorial will take you, step by step, through building a basic Web application in Zope. As we go through the tutorial, we will examine some of Zope's main concepts at work. Using Zope Folder, Script (Python), and Page Template objects, we'll create a simple Web site for an imaginary zoo: the "Zope Zoo", of course! We will develop the Web site as a Zope "instance-space" application. A discussion of instance space is at the end of this chapter, but for now it is enough to know that instance-space applications are the easiest and fastest kind to build, because we can do everything in our favorite Web browser. Goals for the Zope Zoo Web siteAs with any project, we first need to clarify our goals for the Zope Zoo application. The application's primary goal is to create a Web site for the world-renowned Zope Zoo. Furthermore, we want to make the Web site easy to use and manage. Here are some things we'll do:
Beginning with a FolderZope Folder objects provide natural containers and organizers for Web applications. A good way to start building an application is to create a new Folder to hold all the objects and subfolders related to the application. Consider, for example, a Zope folder named Invoices to hold an application for managing invoices through the Web. The Invoices folder could contain both the logic objects - or "methods" - which allow you to add and edit invoices, as well as the actual data of the invoices. The Invoices folder thus becomes a small Zope application. We begin building our Zope Zoo Web site application by creating a Zope Folder object to hold it all together in one place. Step 1: Create ZopeZoo FolderIf you haven't already, start your Zope installation and log into the Zope Management Interface (ZMI) using your favorite browser. (If you are not familiar with the ZMI, refer to the Installing and Starting Zope chapter.)
(For this tutorial, we will ignore the optional Title fields.) The Special Folder Object index_htmlBecause we selected the Create public interface option, Zope not only creates the ZopeZoo folder, but also inserts inside the folder a Page Template object named index_html. If you wish to see it, navigate into the ZopeZoo folder. The stock content of this index_html is quite banal, but we'll change it later. The index_html object is the "public interface" of the ZopeZoo
folder because it creates what site users (the public) will see
when they visit the folder via the Web. You may be familiar with
Apache and other Web servers that return a default directory index
file, often named "index.html", when they receive a request like
Such a URL indicates only a path, and not any particular file.
Servers typically interpret such requests as requests for the
default file, for instance Whenever a Zope folder contains an object named index_html, the return value of index_html will be the folder's "default view". We can display the default view of our ZopeZoo folder a few different ways. Each way gives the same result:
The index_html object may be a Page Template, a DTML Method, a Script (Python) object, or any other URL-accessible Zope object that returns browser-renderable content. (The content is usually HTML, but Zope doesn't care. You can have Zope spit out XML, text or whatever you like.) It will help your understanding of Zope to think of index_html as not only "an object in the folder" but also as "a method or attribute of the folder". That "method or attribute" idea comes from Zope's object-oriented philosophy. If it makes no sense to you, just continue along for now and we'll do some things that hopefully will clarify it. Designing a Navigable ZooOne of our goals is to enable easy user movement around the Web site. A key to this easy movement is a navigation interface that is consistent among the site's pages. In other words, every Web page in the site should present a similar set of hyperlinks, in a similar place on the page, on which users can rely to guide them through the site. We also want to make sure the navigation links are always correct, regardless of how the structure of the site changes. The solution is to design a meaningful site structure, and then create the Zope methods that will dynamically present the current structure to Web users in the form of navigation links. First, let's define the site structure. If the Zope Zoo was real, we might model the Web site's organization on the Zoo's physical or logical design. For our purposes, we will pretend that the Zoo houses three classes of animals. We'll organize the Web site by adding folders inside our ZopeZoo folder. Step 2: Create Site Organization(NOTE: Do not create public interfaces for the folders in this step.)
In Zope's Navigator frame on the left side, you should see an icon for the ZopeZoo folder. (If you don't see it, click Refresh in the Navigator). To view the ZopeZoo folder hierarchy - i.e. our nascent Web site's structure - expand the ZopeZoo folder by clicking the little plus sign next to the icon. Similarly expand the Zoo subfolders. You'll see something like the figure below. Figure 01 Zoo folder structure. Let's assume that, to navigate the Zope Zoo site, our users will start at the default view (index_html) of the ZopeZoo folder. We will call this the "front page" of the site (though "home page" or "start page" would also suffice). From the front page, users should be able to select a link to move to the subfolder for a particular animal class: Fish, Mammals, or Reptiles. Main Site NavigationFirst, let's create a method that will provide every page on the Web site with the "main site navigation", meaning a set of links to the primary site sections (Fish, Mammals, and Reptiles) as well as to the front page. To create consistency among site Web pages, we will call this method on every page. To ensure the accuracy of the links, the method will dynamically return links to the subfolders that exist within ZopeZoo whenever the method is called. We create the main site navigation method as a Zope Page Template (ZPT). Let's build this main navigation template incrementally, and look closely at the use of TAL (Template Attribute Language). Step 3: Create Main Site Navigation Method
In the first two lines of code, we have TAL statements in an HTML
anchor element which create a link to the template's (Zope Page Templates and TAL are covered in the Using Zope Page
Templates chapter, but it may be worth noting here that
The rest of the template code causes the template to render an
HTML anchor for each object that exists within On the But the Each iteration of the Click the Save Changes button, and then click the Test tab at the top of the workspace. The results should look like the following figure. Figure 02 Main Navigation template. A fair beginning: we have links to the front page and to each of
the "animal class" folders. Unfortunately, the index_html and
nav_main Page Templates are listed as well, because they are
also objects in We don't want to show all the objectValues in Although the ZPT Template Attribute Language cannot directly exclude non-folder objects, we can easily include some Python in the TAL statement to do so. Step 4: Exclude Non-Folder Objects
When we once again Test the template, we see only the folder objects within ZopeZoo are displayed. Each link targets the default view of a folder (i.e. the index_html for that folder). We have our main site navigation! But wait. How can these navigation links target the index_html object for each folder if most of the folders lack such an object? The answer lies in the magical Zope facility called "acquisition". Acquisition In Instance-Space ApplicationsAn entire chapter of the Zope Book is devoted to Zope acquisition. Here, we will discuss it more briefly as it applies to the Zope Zoo tutorial. In general, acquisition allows different objects within a Zope application to share attributes. Here, it allows our animal class subfolders to share ZopeZoo's index_html attribute. Do not worry much whether something is an "attribute", "method", or "object". Often, these terms are synonymous. For instance, index_html, the public interface we created for ZopeZoo, is an attribute of ZopeZoo, a method of ZopeZoo, and a Page Template object, all at the same time. What is more important to understand is how acquisition works. In summary, acquisition means that if an object in a Zope application lacks a certain attribute, Zope will search upwards through the object's containers until it finds an attribute by the same name. In the case at hand, if one of our animal class subfolders cannot find an index_html attribute in itself, Zope attempts to acquire the attribute by searching for it in the folder's containers. The first of these containers would be the ZopeZoo folder, which does contain an object named index_html. Zope then applies the found attribute to the animal class subfolder. But to complete this discussion of acquisition, let's say the ZopeZoo object also lacked an index_html attribute. In that case, Zope would search the container of ZopeZoo. This process of "searching a container's container" continues until Zope either finds an index_html attribute or reaches the last container, the Zope Root folder. If Root lacks the attribute, Zope returns an error. But how does Zope resolve the converse situation, when several objects throughout an application have an attribute of the same name? A full-fledged Web site might contain many index_html objects, one for each folder in the site. Zope determines the appropriate object by first looking to the object which is named by the "rightmost" portion of the requested URL. For example, to respond to a Web user's request for the Web URL
We will make use of Zope acquisition now, as we set our nav_main object to be an attribute of every page in our Zope Zoo Web site. Integrating AttributesWe will create two new Zope Page Templates. First, a "header" template, which will contain both our site navigation interface and anything else we want at the head, or top, of every page on our site. Later on, we'll create a "body content" template to hold the subject matter of each page. As you might guess, the header attribute will be shared among pages in the site and the body content attribute will be specific to each page. Then we will bring these components together into index_html and let acquisition turn our application into a halfway meaningful Web site for the Zope Zoo. Step 5: Create More Attributes
The Figure 03 Ugly header HTML Clearly, this is not our intended result, but it is Zope's normal behavior. Zope "escaped" the HTML code returned by nav_main, converting the HTML markup into displayable text. We need to instruct the header template that we don't want to display the raw navigation HTML, but rather to use it as part of the markup code for the Web page. Fortunately, this is very simple in TAL. Step 6: Specify HTML Structure Instead of Displayable HTML Code
Again, click Test. Now you should see a nicely rendered header which looks and works like the nav_main navigation template, because - for the moment - header contains nothing else. Your view should look like the following figure. Figure 04 Nicely rendered header Now, let's modify the body_content template so that it is ready to be included in index_html. We want body_content to be a part of index_html, rather than a full Web page unto itself. Step 7: Prepare body_content Template
Now, to bring everything together, let's assemble the components we've created so far in index_html, thus creating a new default view for the ZopeZoo folder (and by acquisition, the default views for the subfolders of ZopeZoo). Step 8: Assembling the Default View
As you can see, we have designed our site so that index_html is little more than a list of pointers to the attributes that compose a Web page, i.e. the header and body_content objects. To see how our Zope application operates now, open a new browser window and visit: http://localhost:8080/ZopeZoo/ (If your Zope instance is on a remote host, replace Figure 05 Our ZopeZoo default view. Here you start to see how things come together. At the top of this "front page" you see the "main site navigation" links to the front page itself and to the subfolders for the different classes of animals. Click on the Reptiles link to see Zope acquisition in action. When we click the link, the Zope Web server receives a request for
the URL No matter how you say it, Zope attempts to return the results of index_html for Reptiles. Because Reptiles does not contain an index_html of its own, Zope walks up the container hierarchy until it finds index_html in the ZopeZoo folder. Zope then calls the index_html method in the context of Reptiles object. Then, the code in index_html calls the header method. The header template in turn calls the navigation method nav_main. Zope inserts the results of the header method into the default view of Reptiles and then adds the results of the body_content template. Note that all the context-specific TAL statements in our component
Page Templates, and in index_html itself, return results for the
Reptiles object, even though the methods are all acquired from
ZopeZoo. For instance, the TAL expression Calling Methods on FoldersThis context-sensitive aspect of acquisition is a powerful feature of Zope. You can execute any method in the context of any object by visiting a URL that consists of the object's URL followed by the id of the method. We have not tried this feature yet, because we have omitted a method id in the URLs we've called, knowing that Zope will to reply to such requests by calling index_html. But consider a URL from our early Invoices application example,
like: But more importantly, you could call the This facility is used throughout Zope and is a very general design pattern. In fact, you are not restricted to using a folder object as the context of a method. You can call methods on many kinds of Zope objects. Using a little Zope URL trickery, we can even call a method on an object that would not normally be able to acquire it. For information on this URL technique, read in the Advanced Zope Scripting chapter under "Calling Scripts". We can also pass arguments with a URL. For example, the URL
Creating Contextual Subfolder NavigationLet's make more use of Zope acquisition with another method for our Web pages. We will enhance our Web site navigation with a "subfolder navigation" attribute that will appear in the header of each page in the Zope Zoo site, along with the "main site" links we created above. The main site navigation component, defined in the nav_main object, will appear the same throughout the site. It will render differently only if a structural site change occurs within the ZopeZoo folder (i.e. the addition, removal, or renaming of a subfolder). In contrast, we'll now create a navigation component that will change according to the user's location within the site. This contextual navigation will list all the subfolders within the user's site location, allowing navigation deeper into the site's logical hierarchy. We could add the template code for the contextual navigation to the main site navigation Page Template, but for demonstration purposes, we'll create a separate ZPT. Step 9: Create the Subfolder Navigation Method
Click the Test tab. You should see three familiar links listed, this time in an HTML unordered list format (just for variety). Your view should be comparable to the following image. Figure 06 Subfolder Navigation. You should recognize most of the TAL code in this template, with
an important difference: the use of Whereas the TAL built-in name To see the acquisition magic of http://localhost:8080/ZopeZoo/Reptiles/nav_subfolder Hopefully, you will see a list of links to subfolders within the Reptiles folder, like the following figure. Figure 07 Calling nav_subfolder on Reptiles. Similarly, if we call the nav_subfolder template on Mammals, it will display a link to the subfolder, Whales. The method can be called on any object (but makes most sense for folders). With nav_subfolder in the site's top-level ZopeZoo folder, each of the animal class subfolders can acquire it. Adding Elements to the HeaderIn order to make nav_subfolder a part of every Web page on our site, we will call it from the header object (which is itself called from index_html.) A note about our use of a header object may be in order. We could have included the two navigation templates directly into index_html, but grouping them first into a header template has some advantages. For one, we can modify the header component of our Web pages with no need to tinker inside the all-important index_html. More importantly, we can now allow someone else to modify the header (a graphic designer, for example) without fear that they might damage code in index_html. But maintaining a separate header object will prove especially helpful when the Web site has multiple index_htmls throughout its different sections. By maintaining a single all-inclusive header object, we can simply include a one-line reference to it in each index_html. Not only does this make coding easier, but it ensures that the pages throughout the site share a consistent header. The use of a header object in this tutorial is for demonstration and as a design suggestion. In your own Zope application, of course, you are free to do things the way you want. Nonetheless, let's add the subfolder navigation to our Zope Zoo header. Step 10: Integrate the Subfolder Navigation
To see the new results of this method, use a browser to request
Creating a Default View for a SubfolderAs we have seen, a folder object can acquire index_html from its parent folders, or "containers". So far in the Zope Zoo site, all of the folders share index_html in the ZopeZoo folder as their common default view. Furthermore, if we added more empty folders inside our animal folders, a visit to these folders' URLs will also return the same index_html. This is a good thing, because it allows us to have a single, "master" default view from which all sub-objects in the site will acquire, resulting in a consistent Web page header, for example, throughout the entire site. If, for some reason, you want a different default view for a given set of folders, just create a custom index_html object in the top-most folder. Then, the top folder of the section, and all the subfolders within it, will return the custom index_html instead. For example, you could fashion a different default view for the Reptiles section of the site by placing a new index_html in the Reptiles folder. The Reptiles version of the method will "override" the index_html attribute of ZopeZoo. This new default view will apply to not only Reptiles, but also its subfolders Snakes and Lizards, as well as any subfolders you might add within those folders. The default views of other ZopeZoo folders at the same level as Reptiles (Mammals and Fish) will not be affected by the change. That said, it is obvious that we will want different content to appear on different pages of the Zope Zoo Web site. So far, our design ensures that the page elements we want to stay consistent do so. Now we need to differentiate the pages of the site. Instead of creating a new index_html default view for each folder in the Zope Zoo site, our Web pages will continue to acquire index_html from the ZopeZoo object. The important content element for each page is defined by the body_content attributes of the objects in our application. This is an authorial design decision, and I like it. It allows
the separation of the actual subject matter of each Web page from
the non-content elements like the navigation interface and HTML
document stuff (e.g. the Step 11: Customize a Default View
If you click the Test link, you will see only the HTML above. To see the page as it will appear to Zoo Web site users, use a separate browser window to visit: http://localhost:8080/ZopeZoo/Reptiles/ (Replace Through the use of the TAL name Click on the Snakes link in the Reptiles Web page to see what the Snakes object looks like. The default view of Snakes looks much like the Reptiles page. Because it exists within the Reptiles folder, the Snakes folder acquires body_content from the Reptiles folder instead of from the ZopeZoo folder. Also notice that the subfolder navigation element disappears on the Snakes page. Because Snakes contains no subfolders, the result of nav_subfolder is empty when the method is called in the context of Snakes. In your spare time, create default views for the other Zope Zoo animal folders by creating very lavish and detailed body_content objects for each one. But before we leave behind the general composition of our site's Web pages, let's make a couple important refinements. Site RefinementsThe current navigation system in our page header works fairly well, but it has one big problem. Once users go deeper into the site (like from Mammals to Whales), they need to use their browsers' Back function to return to higher levels. We have failed to provide links to allow navigation back up the folder hierarchy. Let's add a "Return to parent" navigation link to allow users to move up the site hierarchy by adding code to nav_subfolder. Step 12: Add a "Return to Parent" link
Once again view the Snakes object via the URL below. (You may need to refresh or reload the page.): http://localhost:8080/ZopeZoo/Reptiles/Snakes/ As you can test for yourself, the Return to parent link allows users to move up from the Snakes section to the Reptiles section and from there to the top-level ZopeZoo section of the site. A problem remains, however: when a user is at the top level of the
site (the ZopeZoo folder) the Return to parent link leads to
the Zope Root folder, which is not part of the Zoo site. We can
fix this by using a TAL Step 13: Modify the "Parent Link" Template Code
We access Python here (using More explicitly, if the object on which this template is called
(the here object) has an id that "is not equal to" ('!=')
If a Now the template uses a TAL Where Am I?Let's tend to one last deficiency in our site. When we added a separate body_content template for the Reptiles section of the site, we lost something important. When a user navigates to the Lizards or Snakes folder, it becomes difficult for them to determine in which section they are located. The ZopeZoo version of the body_content method contained TAL
code that identified the user's current position in the site;
i.e. the code We could add that same TAL code to the Reptiles version of
body_content, but let's assume that we will eventually replace
body_content for every page in the website. Clearly, it would
be easier if we could obviate the need to add Let us therefore shift this code into our header object. That way, we can write the code once and include it automatically with every page in the site that acquires index_html from ZopeZoo (which in turn calls header). (We could put the Step 14: Add a "You Are Here" element
Now when users navigate the pages of our Zope Zoo site, they will
always know in what section they are located. You may wonder why
so many Factoring out Style SheetsA useful design goal in any Zope application is to "factor out" anything that appertains in more than one context within the application. We've done this already by factoring out various navigation elements from our Web pages. To factor something out, we identify it, extract it, and isolate it into a self-contained object. If we factor out something properly, it can be shared among objects that have a use for it. Web site administration becomes easier if application objects can also be factored out along the lines of content, logic, and presentation. In creating our Zope Zoo objects so far in this chapter, we have applied this idea to some extent. For example, we created body_content objects that simply hold content to be included on their respective Web pages. Also, we created Zope logic objects ("methods") which primarily provide behavior for other objects. An example is our template nav_subfolder. When the nav_subfolder method is called on a folder, the method returns HTML links to the subfolders inside the folder, and to the folder's "parent". Objects are rarely 100% pure. They typically include some level of content, logic, and presentation, but the idea is nonetheless useful. Now, we focus on factoring out presentation. If you like Web standards, you know that a good way to supply page presentation information (colors, fonts, etc.) is via Cascading Style Sheets or CSS. The beauty of CSS is that the actual style information can be kept in a single, easily-maintained style sheet. Any changes to the style sheet are immediately seen in all the Web pages that reference it. So it makes sense to factor out the presentation information for our Zope Zoo Web site using a Cascading Style Sheet. And, by keeping this style sheet object separate from any other element, we can easily over-ride it in areas of the site where we want a different visual style. A CSS file is a kind of text file. So, let's add our style sheet to our application in the form of a Zope File object. Step 15: Create the Zope Zoo Style Sheet
This style sheet defines how to display the Step 16: Link Style Sheet to Web Pages
(The Figure 08 Beautifully styled Zoo. Again, due to acquisition, all the Web pages on our Zoo site will share the ZopeZoo style sheet. To change the style information in a part of the site, just create a new style_sheet and drop it into a folder. All the pages from that folder and its sub-folders will use the new style sheet. Building Applications in Zope's "Instance Space"In Zope, there are a few ways to develop a web application. The simplest and fastest way, and the one we have taken, is to build an application in instance space. To understand "instance space", we need to once again think in object orientation terms. The "instance space" is comparable to Zope's Root folder. We can view and manipulate objects in Zope's instance space through the Zope Management Interface, or ZMI, which we access with a web browser. All the components of an instance-space application are instances of Zope classes. We simply use the Zope Management Interface to build the application by adding various instance objects the Zope Object Database. In the figure below, you see the ZMI. The Add drop-down list has been activated, with the selection on Folder. Figure 09 Zope Management Interface Add List. Each object type in the Add list represents a class that has been defined for Zope. When we select a class from the list, Zope prompts us for the information needed to create a new instance of that class. Zope then uses that data to create the instance at the current ZMI location. Instances such as these are inserted into your Zope Object Database (ZODB) and they live there until you delete them. (For more on the concepts of objects, classes, and instances, read the Object Orientation chapter .) For example, the "Folder" class was written by a Zope Corporation engineer. All Folder objects that appear in the ZMI for a given Zope installation are instances of the Folder class. Because every object in a ZODB is an instance of some class, we use the term "instance space" to refer to the conceptual dwelling place of objects in a ZODB. To build an application in instance space, therefore, we use the ZMI to create instances of various Zope classes. We modify the instance objects so they perform the necessary logic, content, or presentation duties, and "glue" them together to create Zope instance-space applications. Instance-Space Applications vs. ProductsIn contrast to building Zope applications in instance space, you may also build them as "Products". Building an application as a Product allows you to "extend" Zope with new object classes that appear in Zope's Add list. Furthermore, Product applications are easier to distribute to other people, and they may allow you to create objects that more closely resemble your "problem space". The Zope Book explores one way to create Products in the chapter entitled Extending Zope. Building a Product is more complicated, however, so in this chapter we explored Zope by building an instance-space application. When you find that an instance-space application becomes difficult to maintain, extend, or distribute, consider rewriting it as a Product. The Next StepThis chapter tried to explain some aspects of how Zope works in practice as well as how to build an application for a simple Web site. Zope has many more features in addition to these, but these simple examples should get you started. |