[Zope-CVS] CVS: Packages/JobBoardEx - Tutorial.html:1.1
Guido van Rossum
guido@python.org
Fri, 22 Mar 2002 17:49:07 -0500
Update of /cvs-repository/Packages/JobBoardEx
In directory cvs.zope.org:/tmp/cvs-serv812
Added Files:
Tutorial.html
Log Message:
Bruce's first take on a tutorial based on this example.
=== Added File Packages/JobBoardEx/Tutorial.html ===
<html><body> <h1>The Job List Example in Zope3</h1>
<h2>Overview</h2>
<p>This application will provide you with a simple but useful example in Zope3,
which can be modified and adapted for your own purposes. It also demonstrates a
pattern for you to follow when creating your own applications.
<p>The Job List allows users to post job information on a site, and to view a
simple list of jobs. A particular job can be selected and viewed in detail. When
a job is submitted for posting, the administrator of the job list can accept or
reject the job. The administrator may also delete existing jobs from the list.
<p>This example was created at the March 19-21, 2002 Sprint at the PythonLabs
offices in Washington DC, lead by Jim Fulton and including Guido Van Rossum,
Jeremy Hylton, Tim Peters, Fred Drake, Barry Warsaw, [The german guy who's name
I can't remember] and Bruce Eckel. The initial draft of this document was
written by Bruce Eckel with help from Jim Fulton, on a train to New York City.
This document is placed in the public domain so that others may freely improve
upon it, but if you do so please submit the result back to Zope Labs.
<h2>Building applications with Zope3</h2>
<p>One way to look at Zope in this case is as the controller of a state machine.
Your system will move through various states based on input (typically from the
user through the web). In a particular state, the system will display a page and
wait for the user to provide input and request a response. Based on the input
and the request, the system will perform an operation and move to a new state.
So your primary job is to tell Zope what to do and what to display for each
state, and how to move from state to state.
<p>In addition, this application will make use of the fact that Zope is built on
an object-oriented database. Anything that we want to be persistent in this
application (for example, the jobs in the list) will be stored automatically in
the Zope database. To do this, all we have to do is inherit a class from the
<b>Persistent</b> base class, as you shall see.
<h2>Entities/Business Objects</h2>
<p>We'll start by defining what can be thought of as the "business objects," or
simply the "entities": those classes that map directly from the conceptual model
of the system. In this case, the model has a <b>Job</b> object which contains
all the information about a single job, and a <b>JobList</b> object which holds
the collection of <b>Job</b> objects, and also controls the addition and removal
of <b>Job</b> objects to itself.
<p>We want to tell Zope how the Job and JobList should be created, edited,
viewed, etc. so that Zope can perform these operations on its own. We do this
by:
<ol>
<li>Creating an interface for each entity. The interface is used by Zope to hook
the various views to the entity.</li>
<p><li>Creating one or more classes that implement that interface. These classes
perform the actual "business logic" for your application, but they don't control
the views (they don't have anything to do with views -- if you asked them about
thier views, they wouldn't know what you're talking about).</li>
<p><li>Creating one or more Zope Page Template files (.pt files), used to create
the views on an interface.</li>
<p><li>Creating a view class that contains both the logic for the presentation
layer and the presentation itself. The presentation is described in the separate
ZPT files mentioned above. The logic for the presentation layer consists
primarily of the actions that occur when the buttons on your web forms are
pressed. In more sophisticated examples than this one, the view class might also
do things like convert data into an appropriate format for display.</li>
<p><li>Modifying the configuration (.zcml) file to give Zope the recipe of how
all these things fit together.</li>
</ol>
<h2>Job Objects</h2>
<p>We'll start by creating the Job Object and its views.
<h3>The IJob Interface</h3>
<p>The primary task of the Job object is to hold information about a job. In
IJob.py, you can see that most of the elements in the interface are Zope
attributes, defined by the Attribute() function in Zope's Interface module.
Attribute() is a way to define attributes in an interface. After all the
attributes, there is a method, approve(), which is used to take the job from the
PendingApproval to the Approved state. These states are defined in the class
JobState.
<p>Note that there is no awareness of display in either the IJob interface or
the Job class.
<h3>The Job class</h3>
<p>Because we want it to be automatically stored in the Zope database, the Job
class is inherited from Persistent. In addition, it is marked as implementing
the IJob interface with the __implements__ assignment. The initialization code
simply creates and initializes the fields from the arguments, and puts the
object in the PendingApproval state. The approve() method changes the state to
Approved.
<h3>The JobView class</h3>
<p>JobView tells Zope how to display a Job. JobView inherits AttributePublisher,
which is the big clue that this is a view class, since that base class is only
associated with view classes. The AttributePublisher class includes an
<pre> __implements__ = IBrowserPublisher </pre>
Statement.
<p>The
<pre> __used_for__ = IJob </pre>
statement allows Zope to check to make sure that the JobView class is only used
for IJob objects during the configuration of the system, and to report an error
if it is used incorrectly.
<p>An instance of a view class is only ever associated with a single object to
be viewed. In this case, it's a Job object so the __init__() stores the
associated Job object and getContext() produces it. (getContext() is called when
...).
<p>Although the <b>index</b> assignment initially appears to be creating a
simple attribute to the JobView class, it's actually creating a method. The
PageTemplateFile() function takes a Zope Page Template description and compiles
it to produce a callable (in this situation) object which thus behaves as a
method. So you can treat that expression as creating a method for the class,
just like <b>def</b> creates a method.
<p>The <b>index</b> method is treated specially by Zope. When you ask for a view
(typically through a URL, by specifying the object and then the view you want on
that object), if you don't otherwise say the particular view you are looking for
then Zope will look for a method named <b>index</b> and use that. So in JobView
we're saying that PageTemplateFile('JobView.pt') is the default view for that
object.
<p><i>Note: the methods that wrap the attributes are necessary to allow access
control for those attributes, until this issue is fixed in Zope3.</i>
<h3>Editing a Job with JobEditView</h3>
<p>The JobEditView class in association with the JobEditView.pt and
JobPreviewView.pt page templates define the process of editing a Job. This
starts on the page produced by JobEditView.pt. If you look at this code, you'll
see relatively normal HTML with some extra "tal" information buried in the tag
fields. Leaving that aside for the time being, we're primarily interested in the
form's "action" and the "submit" inputs (the buttons). The "action" is set to
".", which means (to Zope) "back to the object that created the page." In this
case, that means the JobEditView object. When you combine this fact with the
"submit" inputs, for "Preview" and "Cancel," we produce the desired results. The
name for the preview button is "preview:method". This tells Zope that when that
button is pressed it should call a method named "preview," and since "action" is
".", that indicates that Zope should call the preview method on the page's
current object, a JobEditView. So it calls JobEditView's preview() method, which
you can see will produce another page, created with the expression
PageTemplateFile('JobPreviewView.pt'). If instead you press the "cancel" button,
the name associated with that button is "cancel:method" so the JobEditView
cancel() method will be called. Note that this is a regular def method.
<p>The submit() method is called from the JobPreviewView.pt in the same way. The
same edit() and cancel() methods are also called from JobPreviewView.pt.
<p>You can see that the view classes don't just display pages, but they also
contain control logic that will respond to actions on those views. If you are
thinking in terms of model-view-controller, what you see here is only a partial
fullfillment of that idea. True, as much of the "controller" aspect as possible
is built into the standard Zope code (and is thus "under the covers" from the
perspective of our application), and the goal is to just tell Zope how to do the
controlling for you. But it's inevitable that you must put <i>some</i> control
code in your system, and it turns out that control code is typically highly
coupled with view code, and so it's cleaner to include the control code, such as
the cancel() and submit() methods, in the view class. Thus we end up with more
of a view/controller. The trick is not to give into the temptation to put all
your logic in the view class, because doing so leads you down the Visual Basic
path, which inevitably produces applications that are problematic to maintain
and that don't scale well.
<h2>IJobList and Joblist</h2>
<h2>Viewing a Joblist</h2>
<h2>Page Templates</h2>
<p>In the previous section there was a bit of introduction to page templates.
Here we'll go into detail and explain how page templates are associated with
objects, and how they extract information from their objects.
<p>...
<h2>Tying it all together with the configuration file</h2>
<p>Finally, we need to tell Zope what's what with all these classes -- how they
are supposed to interact with each other and what permissions to allow.
<p>...