History for SandBox
??changed:
-
Zope 2.2 Product Developer's Guide - Chapter III - Python Products
Approaching the Zope Product API
Extending Zope is quite simple compared to many other
environments. Since Zope is based on the clear, elegant Python
scripting language, Zope extensions are quick to develop and clear
to maintain.
However, since they can leverage the full power of the Zope
framework, writing a Zope Product is more complicated than the
extremely simple extensibility of External Methods. Fortunately
the step up in complexity is much less than the dramatic increase
in functionality.
In this tutorial we are going to build a poll Product.
The included Python and DTML files show some of the steps in the
development of a sample poll Product. The 'Poll.tar.gz' file is
the completed Product. To install the completed Product ungzip and
untar it inside your Zope directory. Then shutdown and restart
Zope to start using the sample poll Product.
What you need to know before you begin
Before you read this tutorial you should have a basic
understanding of how Zope publishes Python objects. You should, of
course, know Python reasonably well. If you've used object
publishing before, perfect. If not, you might want to read an
introduction to object publishing before you begin, because the
Zope Product API is just a special case of object publishing
programming. Finally you should have a reasonable familiarity with
Zope itself. You should understand how to do things like create
Folders and Documents, and use DTML to call methods, and set
properties. The Zope Manager's Guide is a good place to learn how
to use Zope.
Defining and analyzing your task
Writing a Zope Product, creating a website, building a database,
all these activities are tasks which are done to solve specific
problems. If you want to do a good job building a solution, you
must understand your problem first. In the next section we'll talk
about different approaches to solving problems in Zope. However,
its a good idea to start with a description of the problem.
Here's a short problem description and analysis for our poll
project.
Requirements
Customers manage their areas of a Zope site and want to
provide information services. In this case, a customer
wants to collect feedback from users regarding a certain
topic. That is, they want to conduct a poll.
A poll contains ballot with question with one or more
answers. The ballot is the HTML presentation of the
question, answers, and control elements. Users can select
only one answer for a poll. Users can either vote in the
poll or view the current results or both. The results at
any time are displayed by default as text using an
attractive out-of-the-box format. The appearance of the
ballot should be customizable, and the ballot should be
able to be embedded in other documents.
Finally, a poll should allow voting by only a select group
of users that have the correct credentials. That is,
should be able to be private.
Analysis
The Poll appears to have actors of Customer, User,
Privileged User. The Customer actor is someone able to add
and manage Zope content. The User actor is someone with
the Anonymous role that interacts with Zope. A Privileged
User appears to be a non-Anonymous Zope User. This means
that the Poll should expose some permissions for
operations such as voting and presenting results, allowing
the permissions to include various roles.
The Poll has a number of crisp abstractions. A Poll
represents all the state and behavior needed to accomplish
the task. A Ballot appears to be Document with some DTML
scripting in it. A Question might not be a separate
object. Answers might be separate objects that leverage
the normal Zope Folder interface to manage them.
Is the Product API right for the project?
After you have described and thought about your pojects, the next
step is to evaluate whether your idea would work well as a
Product, or if it could be better implemented some other way. In
our case we would like to build a poll product like you see on
Slashdot and many other sites.
We could probably build it with SQL Methods and DTML without too
much difficulty. Though this would require mucking around with the
RDBMS whenever you wanted to create a new poll or modify an
existing poll.
We could probably build a simple polling facility just using DTML,
Documents and maybe properties. Again, this approach would
probably involve more administration hassles than we would like.
Using a Zope Product we could provide easy manageabiliy to Zope
users who want to create their own polls. Using a Product also
gives us fine control over access controls, and gives us room to
refine and develop our poll over a period of time.
So we have a marginal case for turning our idea into a Product.
OK, good enough let's get going.
Getting started in plain old Python
Now that we have decided to harness the full power of the Zope
Framework to create a reusable Zope Product, the first thing to do
is to write a prototype in Python. This prototype will try to
capture the essential algorithms of our product. It will also
allow us to test the guts of the product and make sure they are
sound before we start confusing ourselves with exotic Zopisms.
Here's a first cut at what a poll class might look like::
class Poll:
"A multiple choice poll."
def __init__(self,question,choices):
self.question=question
self.choices=choices
self.votes={}
for choice in range(len(choices)):
self.votes[choice]=0
def vote(self,choice):
"vote for a choice"
self.votes[choice]=self.votes[choice]+1
def total_votes(self):
"total number of votes cast"
total=0
for v in self.votes.values():
total=total+v
return total
def votes_for(self,choice):
"number of votes cast for a given choice"
return self.votes[choice]
Basically our 'Poll' object keeps track of what is going on in a
dictionary that maps choices to numbers of votes. The 'vote'
method actually casts a vote. 'votes' is a dictionary keyed the
the choice index which keeps track of the number of votes for each
choice. The 'total_votes' and 'votes_for' methods just report
statistics about the poll.
Of note is the fact that we haven't made any effort to keep people
from voting more than once. There are a number of ways we could
handle this, for example we could somehow record the identity of
everyone who voted. Then when someone tries to vote we could look
them up in our list of people who have voted. This approach has
some problems since it is often difficult to reasonably identify
someone. Let's leave this problem unresolved for now.
Testing our initial implementation in Python
An original purpose of Zope was to hide the publishing details and
allow Python developers to think in terms of Python. Thus, at this
stage let's go into Python and test what we have.
OK, so does our first attempt at a poll work? Let's exercise it a
little::
def test():
p=Poll("What's for breakfast?",["spam","eggs","toast"])
p.vote(0) # vote for spam
p.vote(2) # vote for toast
p.vote(0) # vote for spam
print p.total_votes() #returns 3
print p.votes_for(0) #returns 2
You can find this code in the included 'Poll1.py' example file.
Run it and see for yourself that it works.
At first glance it seems pretty reasonable. Though if you try to
vote for a non-existent choice you get an 'KeyError'. Also there's
nothing forcing you to initialize your poll class with reasonable
arguments. Well that's OK for now. But we may want to add some
error handling later. Zope provides a number of error handling
features, like automated user notification of errors, and
transaction handling to abort transactions when uncaught
exceptions are raised.
Adding a user interface
OK, now we have a semi-functional poll, let's add a user interface
so that people can actually use it through the web. The normal
Zopish way to do this is to use DocumentTemplate. A product can
[1131 more lines...]