[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>...