Lesson 1: Getting Started

  1. Introduction
  2. Files:

Teacher: Paul Carduner

Students: Brittney, Will, Preetam, and Linda

Introduction

Paul:

Good morning, everyone, let's start learning to program in Zope3!

Zope stands for the Z Object Publishing Environment. It is the web framework that we will be using to write our web application. Zope's greatest strength is its component architechture. The component architecture allows us to break things up (programmatically) into different parts that work with each other yet independently. For example, we can use the component architecture to separate the data storage, business logic, and user interface so that programmers don't have to worry about how things look, and web designers don't have to worry about how things work.

In addition, with a component architecture, an application is comprised of chunks of functional code, which can be reused easily in other third party applications.

So to begin with, I will assume you have each read over the user stories and have a running instance of zope3 in your home directory in a subdirectory called zope3. You will need an editor you can use to edit python files. Vim or Emacs are always good choices. Starting in your home directory, do the following to begin setting up the zcontact application:

  1. cd $HOME/zope3/lib/python
  2. mkdir zcontact
  3. cd zcontact
  4. echo "# Makes this folder a python package" > __init__.py (to make the zcontact directory a module)
  5. vi interfaces.py

A note about the __init__.py:
It makes the zcontact directory a python package. So that if a python interpreter is opened while in ~/zope/lib/python, you can import the zcontact package like so:

>>> import zcontact

Try importing zcontact without __init__.py to see what happens.

Now create and edit interfaces.py so that it looks like this:

import zope.interface

class IContact(zope.interface.Interface):
    """The interface for a contact."""
    lastName = zope.interface.Attribute("A contact's last name.")

The triple quoted string, """The interface for a contact.""", is used to specify a particular kind of comment, called a docstring because it is used to provided documentation for the object in which it is found. Docstrings must be written immediately underneath the class definition header.

Brittney:

Stephan Richter, on page 52 lines 01 & 02 of the Zope3 Developer's Handbook, uses a different syntax for the import. He uses:

from zope.interface import Interface

Can you explain the difference?

Paul:

You can use either method of import. Since many of you are fairly new to writing a large Python application like this, I thought it would be clearer to make the source of the classes we are importing explicit, rather than bringing them into the main namespace of the program.

Will:

Why do we use "A contact's last name." when creating the lastName attribute? Is it a default value or something?

Paul:

That is a good question. The string we pass to the zope.interfaces.Attribute object is not a default value. The string actually serves as documentation for that attribute. You might ask, Well why don't we just put a comment above lastName instead? Unfortunately, when you use regular python comments, it is difficult to figure out what piece of code the comment is talking about. However, if we make the comment as part of the code itself, then it will be available to us programmatically.

Brittney:

Is interfaces.py supposed to do anything?

Paul:

At the moment, no, it won't do anything. If you wanted to run it through Python to make sure you don't have any syntax errors, you could run python interfaces.py. If Python doesn't report any errors, then your syntax is OK. As soon as we begin to import things though, you would need to add the following line to the bottom of your .bashrc file so Python can find the zope source:

PYTHONPATH=/usr/local/src/Zope3/src
export PYTHONPATH

Note that in this case we are assuming you have installed zope as described in https://wiki.ubuntu.com/LearningZope3. If you have installed zope somewhere else, you will need to adjust the PYTHONPATH accordingly.

Preetam:

You can check that this worked by typing:
echo $PYTHONPATH

Paul:

All right, is everyone ready to move on? Good, let's begin.

The first thing to understand is that Zope is a content management system, so the most important thing we are going to be working with is content, specifically what are called content objects. A content object is any type of information we want to store as a complex python class. For example, what we are working on now is a representation of a Contact that can be stored in the system.

For every content object, there will be interfaces which define what features the content object will provide. That is, the implementation you come up with for this object must fit a contract defined by an interface.

Interfaces are also heavily used for documentation purposes. So, in the interfaces.py file, you see the IContact interface, and we have defined one attribute, lastName. That means that all implementations of the IContact interface must have a lastName attribute.

Brittney:

I don't understand, where is the implementation of IContact?

Paul:

We haven't written the implementation yet. That is the next step we are going to do.

Create and edit a new file in your zcontact directory called contact.py. This file will contain our implementation of IContact. Add the following code to your file:

import zope.interface

import interfaces

class Contact(object):
    """Implementation of IContact"""
    zope.interface.implements(interfaces.IContact)

    lastName = u''

Notice that we are importing interfaces which is the code that containes the IContact interface. We create a class named the same as IContact except without the I because this is an implementation. Although the standard prefix for interfaces is I, there is no prefix for implementations. The line that says zope.interface.implements(interfaces.IContact) is used to connect this implementation to the interface it is implementing. In one sense we are registering this class as implementing the IContact interface. Finally, we include a lastName attribute in the class definition because the IContact interface requires it.

Brittney:

Why is there a u in front of the quotes for the lastName attribute?

Paul:

The u denotes that this empty string is a unicode string. It is very important in web applications that all strings in the program be unicode strings. Unicode strings allow us to put characters from other languages like Arabic or Chinese rather than just the ASCII characters. Zope often requires strings to be unicode, and will even throw errors if the strings are not unicode strings.

Let us continue. We have now defined a simple Contact. The next step is to create and edit a file called configure.zcml. ZCML stands for Zope Configuration Markup Language. It is an XML language we use to glue all our code together. You will see what I mean by this shortly. The first lines should be:

<configure xmlns="http://namespaces.zope.org/zope"
     xmlns:browser="http://namespaces.zope.org/browser">

The configure tag is always the first tag in a zcml file, just like <html> is always the first tag in an html file. In this tag we specify the xml namespace for the rest of the document. The namespace defines what kind of tags will appear in the configuration file. The first namespace we define is the zope namespace, which allows us to configure the content objects we created, like Contact. Since there can only be one default namespace in a configuration file, additional ones have to be specified using a prefix. For the browser namespace we specify the prefix browser by writing xmlns:browser instead of just xmlns. The browser namespace allows us to configure how objects get displayed in a web browser.

The next lines to put in the zcml file are:

  <class class="zcontact.contact.Contact">
    <require permission="zope.View"
            interface="zcontact.interfaces.IContact" />
    <require permission="zope.ManageContent"
             set_schema="zcontact.interfaces.IContact" />
  </class>

The class tag is used to register and configure a particular object within zope. By default, zope does not allow access to any attributes defined in a class, so we have to explicitly define permissions for them using the require tag. With the first require tag we are saying that you must have the zope.View permission in order to read the attributes defined in the IContact interface. The second require tag says that we must have the zope.ManageContent permission in order to change the attributes defined in the IContact interface. We will talk about why it says set_schema instead of set_interface in a later lesson. We will also cover more on permissions in later lessons.

Finally, the last lines to add are:

  <browser:addMenuItem
      title="Z Contact Page"
      description="Add a Z Contact Page"
      class="zcontact.contact.Contact"
      permission="zope.ManageContent"
      />
</configure>

Don't forget to close the configure tag at the end of the zcml file. The browser tag allows us to add a link in the ZMI, or Zope Management Interface, under a special Add Menu which allows us to actually add an instance of the Contact object to our database. We will talk more about how the database works later.

I see we are almost out of time, so now would be a good time to stop. But before we go, I want you to make a file called zcontact-configure.zcml, and, in addition to having it in your zcontact directory, have a copy in your zope3/etc/package-includes folder. The file only needs one line: <include package="zcontact" />. This lets Zope know about our package so that it gets included when the zope server starts up.

Now you should be able to start up your zope server and log in to the ZMI. If the zope server doesn't start, then there is something wrong with your code. Once you are logged into the ZMI, you should see a link in the add menu called Z Contact Page. When you click on it, you will be able to enter in a name for the new object and actually create it.

Now would be a good time to show you another way you can look around the zope3 source. Start zope (by executing ./zope3/bin/runzope from your home directory). Now point your web browser at http://localhost:8080/++apidoc++. After you login you will be in the Zope 3 API Documentation browser. Click on Code Browser and then the Browse Zope Source on the bottom frame on the right. You will be able to click your way through the entire source tree using your web browser.

For homework, look over the Writing New Content Objects chapter (http://wiki.zope.org/zope3/contentobject.html) from the Zope 3 Developer's Handbook. (http://wiki.zope.org/zope3/Zope3Book). You will see what we did today in class in that chapter.

Well, see you all next week. Class dismissed.

Files:

When you click on the links below you will be presented with text versions of these files. If you choose to save these files from your web browser, be sure to put the files in the proper directory structure.

  1. zcontact/interfaces.py
  2. zcontact/contact.py
  3. zcontact/zcontact-configure.zcml
  4. zcontact/configure.zcml
  5. zcontact/__init__.py

A gzipped tarball of all these files is located here: lesson01.tgz

Front Page

Next Lesson