RFC II: pre-creation view solution [was Re: [Zope3-dev] RFC: preCreation view, postCreation view use cases]

Gary Poster garyposter@earthlink.net
Tue, 2 Apr 2002 13:45:11 -0500


On Thursday 28 March 2002 10:17 am, Guido van Rossum wrote:
> I'm curious how you can provide a
> general framework for this feature -- maybe a look at the jobboard
> example (checked in as Packages/JobBoardEx on cvs.zope.org) would help
> you.

OK: I've built one.

I need to have a general blessing, particularly from ZC but also from the 
community, before I merge this into the head.  It changes a few fundamental 
pieces of code.  I would like to have some kind of approval very soon, so 
that my merge (already before Steve A.'s And Stephan's recent mega-checkins) 
isn't too nightmarish.

Interested parties may checkout Zope3 from the 
"gary-pre_create_views-branch" branch, and checkout JobBoardEx from 
"gary-pre_create_views_example-branch"

To try it out, once you have everything set up:
 * You can see the default pre-creation view by typing in something like
   http://127.0.0.1:8080/.ZPTPage.;create 
   within the container to which you want to add an item.  This will work
   with all standard addable items.

 * See this work with the job board by adding a job board, then clicking
   add job: you can see in the URL that we are using the "create" namespace.  
   Add a job and then click on "Approve submitted jobs" to see the new item
   (note that approving doesn't work: it doesn't appear to be pertinent to my 
   changes and I don't want to spend time looking further into it until this 
   approach is approved)

Here's an overview of the approach: kinds of things changed, pros, and cons.

Kinds of things changed:

 * I changed all of the factory/provideClass code in Zope.App.ZMI to accept  
arguments.

 * I added a namespace, "create," to PublicationTraversal.  This namespace 
   returns an Addable corresponding to the addable id (such as is used in the 
   standard adder;view html)

 * I added a property to the zcml zmi:factoryFromClass tag, marker_interface,
   in which you name the marker interface you want to use to attach creation 
   views for this factory

 * I did a fishy __implements__ trick to add marker interfaces on Addable
   instances (see cons below)

Pros

 * All previously written tests pass

 * You can now have a view to create an object.  As a view, you can then use 
most any standard mechanism you desire to create the view (including Zope 
2.5-ish session objects for wizards, presumably).  You can also define both 
default creation views and other creation views.

 * You can therefore gather arguments to create a class without instantiating 
it: this is important for a number of reasons.  First, it allows you to 
cleanly use standard Python classes with required __init__ arguments.  
Second, it allows you to gather those arguments incrementally without calling 
a potentially expensive instantiation until you are ready.

 * The creation view also has complete per-product control of where the 
product goes after creation (i.e. the post-creation use case is taken care of 
per-view at least)

 * Objects that do not need a special creation view still have a default 
creation view (i.e., http://127.0.0.1:8080/.ZPTPage.;create as above)

 * Fairly simple set-up: just build a marker interface, build a pre-creation 
view, and set up the marker interface and view as in the following zcml 
excerpt (*everything* is standard except the "marker_interface" declaration): 

<zmi:factoryFromClass
	name=".Job."
	permission_id="Zope.Public"
	title="Job"
	marker_interface=".IJob.IJobCreator." 
/>

<!-- *** Views *** -->

<!-- pre-creation job view -->

<browser:defaultView
    for=".IJob.IJobCreator"
    name="create"
    factory=".JobView.CreateJobView"
/>

<security:protectClass
        name=".JobView.CreateJobView"
        permission_id="Zope.Public"
        methods="index, action"
/>

Cons

 * THE BIGGIE: I use the Addables as the traversed objects to get the 
creation views.  This has a lot of advantages, in that the creation view then 
has all of the necessary Addable creation information available up the 
acquisition chain.  In order to have this work, I perform, arguably, a hack: 
I attach marker interfaces to the Addable *instances* in __implements__, as 
they are being added from the zcml.  This conflicts with nothing at the 
moment, and I tried to accomodate future changes (i.e., adding a true 
__implements__ declaration to the Addable class).  However, it is a hack, 
since __implements__, even for these marker interfaces, is for classes...

 * The approach requires you to create a marker interface *if* you need a 
special creation view (if you want to use the default then no marker 
interface is needed).  Very easy, but yet another thing to have to do.

 * the zcml property name "marker_interface" is not descriptive enough, both 
because it is a pre-creation marker interface, and because in terms of the 
implementation it could also be a tuple.  Ideas?

 * It would be nice if one could automatically create marker interfaces from 
the associated (created) class __implements__ so that creation views for 
superclasses could be automatically available.  I don't see an easy clean way 
of doing this, though.

 * In the JobBoard example, since there are no placeful factories yet, Jobs 
are available everywhere.  I made it so that Job creation views could only be 
shown in the JobBoard for a lark, but placeful factories will eventually be 
needed, of course

Things I could do if this is approved

 * tests specifically for the changes (factory tests involving arguments, in 
particular)

 * I could easily change Container's "adder;view" to use this 
mechanism

 * I could have added a preview creation view for the JobBoard example: that 
kind of thing wouldn't be too hard

Epistle over.  Thanks for reading.  Please give feedback asap to keep me from 
too much merging hell. :-)

Gary