[Zope] Re: creating ZClasses with external method

Brian Lloyd Brian@digicool.com
Tue, 13 Jul 1999 09:53:00 -0400


> Alex> I still cannot create ZClasses with an external method (using
> Alex> aquisition) Since I can do it with ZPublisher.Client 
> and by typing
> Alex> in a URL into my browser, but not via an external 
> method, this is
> Alex> really seeming like a bug. Should I report this to Collector?
>  <snip>
> 
> It looks so easy in retrospect :-) This method below works a like a
> charm. I adapted this from Brian Lloyd's post today to zope-dev
> (subject: ZClasses instances become detached from class).
> 
>   def addFoo(self, id='a_obj', REQUEST=None):
>       """ """
>       # Get the actual destination object, using the this()
>       # method, to be sure we get it in the right context..
>       self=self.this()
> 
>       # Create the new instance
>       newob=self.Control_Panel.Products.Foo.foo(id)
>       # redundant?
>       newob.id=id
> 
>       # Set properties based on the contents of the Add form
>       newob.propertysheets.info.manage_changeProperties(REQUEST)
> 
>       # Add the new instance to the target object
>       self._setObject(id, newob)
> 
>       return 'OK!'
> 
> Question: why the heck does one need to call self=self.this()? In what
> cases does it make a difference? That's just weird. Is this() def'd in
> SimpleItem.py?
> 
> Alex Rice

Well, using the this() method is necessary because of the
behind-the-scenes trickery that is actually going on when
you create a new object. If you look at the target URL 
when you create an object, it looks something like:

  /myFolder/manage_addProduct/OFSP/folderAdd

The 'manage_addProduct' method of ObjectManagers is
actually a FactoryDispatcher object that masquerades
as a method. It implements the trickery to find the
right product and constructor, etc. The upshot is 
that when you actually submit the add form for an
object, it is submitted to a constructor that is
several levels deeper in the URL hierarchy than the
actual destination where you are creating the new
object. It is going through the destination object,
a factory dispatcher, and the factory for the object
itself to get to the object constructor.

The constructor, of course, often wants to use the
'self' object, and expects it to be the parent object
that the new object will be living in. However, as
you can see above, the 'self' that you see in your
external method is very likely going to actually be
the _Factory_ object (which actually contains the
constructor). 

Because of that, you need to set 'self' to the actual 
destination where the object will be living - we used 
to call self.Destination() to get that object, but the 
this() method has been made an alias for the old 
Destination() method. Using "self=self.this()" in your
external method will set 'self' to the actual destination
object. While this may be kind of unintuitive, it is a
necessary evil that you have to set the destination to
the right thing yourself, because your constructor may
want to use attributes of the Product itself. For example,
above, you created your ZClass above with the line:


  # Create the new instance
  newob=self.Control_Panel.Products.Foo.foo(id)


It would be better in this case (to avoid hard coding
product names and locations) to use 'self' in its initial
context (the factory), and defer setting self to this()
until after you are done using attributes of the Product/
Factory:

   def addFoo(self, id='a_obj', REQUEST=None):
       """ """

       # Create the new instance. Here, self is still
       # the Factory, which can acquire the ZClass that
       # we want to instantiate (foo) from the Product.
       newob=self.foo(id)

       # Now we're done using things from the Factory or
       # Product, so set self to the actual destination
       # object where our new instance will live.
       self=self.this()
 
       # Set properties based on the contents of the Add form
       newob.propertysheets.info.manage_changeProperties(REQUEST)
 
       # Add the new instance to the destination object (which
       # is self at this point).
       self._setObject(id, newob)
 
       return 'OK!'


The gory details of how this stuff works can be found in
the App package in Zope (Product, Factory, FactoryDispatcher,
etc.) Hopefully this explanation helps a bit - let me know
if it did more harm than good, and maybe I can get Jim to 
explain it better... :^)


Brian Lloyd        brian@digicool.com
Software Engineer  540.371.6909              
Digital Creations  http://www.digicool.com