Writing Zope Products
helloworld.py is a perfectly legal Zope product; we can install it in lib/python/Products, and Zope will not mind. Unfortunately, Zope also will fail to notice that helloworld.py is there at all, will not add it to the Add selection list and generally will ignore the work we did in writing our product. It's clear that we will need to beef up our skeletal product if we want it to interact with Zope. We will call this enhanced version the “smallhello” product.
For starters, we must change the structure of our product from a single standalone module file (helloworld.py) into a full-fledged Python package. A package is a directory (smallhello) within the Python search path (defined by the variable sys.path) containing one or more Python source files. In our case, the smallhello directory will contain two files: a smallhello.py file that is quite similar to helloworld.py (see Listing 2) and __init__.py (see Listing 3), which initializes and helps to register our object.
The __init__.py file first imports smallhello.smallhello, defining the module's methods and attributes. But the crucial part of __init__.py, at least as far as Zope is concerned, is the initialize method. After Zope discovers and imports smallhello, it invokes smallhello.initialize, passing it a ProductContext object (called “context”). In other words, initializing an object results in that object registering itself with the server.
The initialize routine itself is pretty straightforward, although our version does some rudimentary error trapping (using try/except) to ensure that things work correctly. Our smallhello product only passes two arguments to context.registerClass: the finalhello.finalhello object that we want to add and then a tuple of constructors that should be invoked when we want to create a new instance of our product. Remember to include a trailing comma if you pass a single item to constructors; otherwise, Zope will fail to load the product.
The constructors parameter is just one of a number of named parameters that we can pass to context.registerClass to customize the way in which our object is registered with Zope. For example, we also can pass an icon parameter that tells Zope which graphic (a filename inside of our package directory) should be placed next to instances of our package within Zope.
Turning helloworld.py into smallhello.py (see Listing 2) requires some small changes. We start by adding a method that allows Zope to create new instances of our product. By convention, such management-related methods begin with manage_, so our method is called manage_smallhello. This is the same method that was named in the constructors tuple passed to context.registerClass.
The most significant change to our smallhello class is also one of the least obvious: we have made it a subclass of OFS.SimpleItem.SimpleItem, one of the Zope product base classes provided as part of the Zope package (in the OFS.SimpleItem package). Without inheriting from SimpleItem, many things—from cutting and pasting of objects to acquisition—would fail to work as we expect, if at all. There are several possible base classes from which your products can inherit; SimpleItem, as its name implies, is designed to be the simplest and most straightforward of the bunch.
While modifying smallhello.py, I decided to add two other methods that produce content. One of them, other_html, produces output that is similar to index_html—except, of course, index_html is displayed by default when no other method is specified, while other_html only is called when explicitly named in the URL.
I also added a foo_file method that demonstrates how to return the contents of an HTML (or DTML) file from disk. It can be annoying and frustrating to put all of your HTML inside a Python module file; this way, you can keep the DTML files inside of the package directory but modify them independently of the program itself. Note that we had to import the HTMLFile method from the Globals package in order for this to work.
I modified the __init__ function in smallhello.py to take three arguments: self, id and title. (Previously, it only accepted self and id.) The __init__ function is invoked each time a new instance of smallhello.py is created, which is done through a call to manage_smallhello. Inside of manage_smallhello, our call to self._setObject sets the object ID to a generic smallhello_id, with a title of smallhello_title. Because we hard code the ID in our example, and because IDs must be unique within a folder, this means that we only can have one instance of our smallhello product in a given folder. There isn't enough space here to describe how to read and write parameters, but a quick look at the examples mentioned in the Resources section should make it obvious how to do this.
After the call to self._setObject, manage_smallhello then redirects the user's browser to the main (index_html) method. We could display some output instead, redirecting the user's browser to another method in our object, but I took the easy way out and decided to send users to /index.html on our site.
After you have installed the smallhello product, you can stop Zope and restart it again. You should see “smallhello” near the bottom of the Add menu; selecting it will send you to the index.html page on your Zope site. Because we haven't made our product as user-friendly as it could be, you will have to enter the URL manually (index_html, other_html or foo_file) in your browser. But there isn't any reason why these pages cannot contain links to each other or why other pages on the site cannot link to them.
What do you know? We've created a Zope product!
Practical books for the most technical people on the planet. Newly available books include:
- Agile Product Development by Ted Schmidt
- Improve Business Processes with an Enterprise Job Scheduler by Mike Diehl
- Finding Your Way: Mapping Your Network to Improve Manageability by Bill Childers
- DIY Commerce Site by Reven Lerner
Plus many more.