[Zope-dev] ZPatterns: Non-ZODB storage and Racks

Phillip J. Eby pje@telecommunity.com
Thu, 09 Nov 2000 10:18:58 -0500


At 09:18 AM 11/9/00 +0200, Roch'e Compaan wrote:
>
>I set "loaded by accessing attribute" to the attribute "id".

You should only do that if you want an "infinite rack".  That is, one which
contains all possible objects.  Using "id" means that if the Rack tries to
see if an object exists, it will *always* exist, whether it really exists
or not.  (Because the "id" attribute will always exist.)  Given your
example SkinScript below, you should probably be using "name" as the
attribute to load off of.  Then, the rack will only return an object if it
exists in the SQL database.  (Btw, you should probably be using WITH QUERY
in your SkinScript.)


>Storing items
>in the RDBMS works fine.  But when I try to retrieve them with
>getPersistentItemIDs() nothing is returned?

That's because getPersistentItemIDs() only returns the id's of objects
which are stored in the ZODB.  That method exists only so you have some way
of iterating over objects when you're using the "stored persistently"
storage mode.  It can also be used to find the id's of objects which have
some of their attributes or propertysheets stored persistently.  It is
*not* a "get me everything in the rack" method.


>  I have a skinsript method
>getCustomer:
>
>WITH getCustomerSQL(CUSTOMER_ID=self.id) COMPUTE id=CUSTOMER_ID, name=NAME
>
>and getCustomerSQL is a SQL method.

If your objects are stored in an SQL database, then to get a list of
objects you need to use an SQL method.

Here is the "recommended approach" for iterating over objects in ZPatterns:

1. Define domain-specific retrieval methods in your specialist.  For
example, "getPastDueToDoItems()" and "getAllToDoItems()".

2. Create methods in your rack(s) which actually implement the retrieval;
these may be named the same, or depending on your application, they may be
smaller chunks of code which you aggregate or pass parameters to, in order
to implement the higher-level functioning.  If a given rack uses ZODB
storage, its implementation methods may use getPersistentItemIDs(), but
they should otherwise be using SQL methods, catalog searches, or other
means of retrieving a list of objects.


3. Have the domain-specific methods call the methods in the racks to
implement the desired behavior, and have *all* other code call the methods
in the Specialist.

The key here is to remember that:

1. A Specialist is a singleton object (i.e. only one instance per app) that
is responsible for dealing with objects of a particular interface.
Specifically, it is responsible for:

 a) Creating objects which supply that interface, according to given criteria

 b) Retrieving objects which supply that interface, given an identifier

 c) Manipulating groups of objects which supply that interface, through a
domain-specific API (e.g. getPastDueItems(), purgeCompletedItems(), etc.)

 d) Providing an application-level UI for all of the above, as/when needed
by the application

 e) Providing UI "snippet" methods to create selectors (e.g. form fields,
dropdowns, etc.) for other objects to use in their UI's, so that they can
relate to or otherwise interact with objects the Specialist is responsible
for.

(Notice, by the way, that this list effectively puts everything that an
application integrator might want to customize all in one place...)

2. Racks are essentially *private objects* belonging to the Specialist to
help it carry out its responsibilities.  Each rack is responsible for
retrieving objects of a *particular class and storage location* which
implement the interface the Specialist serves.  Multiple racks are used
when there is more than one class that implements the interface in the
application.

3. Because Racks belong to the Specialist, it's okay for the Specialist to
"know" things about its racks as a whole (e.g. which ones provide what
concrete classes, what search order to use, how to combine the results of
queries from them, etc.), but it should still delegate the actual
*implementation* of behaviors to them wherever possible. 


If you follow this approach, you will end up with an application whose
functioning is cleanly divided, and any developer familiar with ZPatterns
will know where to call things, and where to go to change implementations.
Further, if that developer needs to change from an SQL database to using
the ZODB, or vice versa, or change from one database schema to another,
they will be able to do so without changing code outside the racks, or at
most, the specialist, and all other application code, including your
classes and ZClasses, will be unaffected by the changes.