[Zope-CMF] Reference Objects in CMF

Tres Seaver tseaver@zope.com
Mon, 10 Dec 2001 23:06:07 -0500


Carl Rendell wrote:

> This is a second posting with [hopefully] more clarity in my explanation 
> of what I'm looking for.
> 
> Initially I thought I would use something that acted like a symbolic 
> link (symlink), but after searching for and looking at the behavior 
> scripted in Shane Hathaway's 'PortableHoles' product I realized that 
> symbolic link behavior is not what I'm after.
> 
> I don't want an object to appear in two places at once. What I really 
> want is a 'reference' to an object that has it's own meta information 
> (primarily expiration), and localized security behavior [the reference 
> object not the 'target'].
> 
> It appears that what I need is to get the unrestricted traversal path 
> for the object I want to reference (is this essentially the Unique ID?), 
> and then when accessing attributes of the referenced object I need to 
> use that path to call the object directly. Something on the order of 
> getObject(), but with the unique id as opposed to the data_record_id_.
> 
> Can someone let me know if I'm on the right path, and perhaps reference 
> the python or dtml calls that would facilitate this behavior?
> 
> Thanks,

Carl,

The method you are looking for is 'unrestrictedTraverse' (if
calling from trusted code, using a trusted path) or
'restrictedTraverse' (if either is untrusted).  The second has
exactly the same effect as the first, except that each step in
the traversal path has a security check applied to it.

The result of either call will be an object (if successful) or
the functions will raise an exception (usually an AttributeError
or KeyError, or else Unauthorized).

A simple Python proxy can be built around this method::

   _marker = []

   class ObjectReferenceError( Exception ):
       pass

   class ObjectReference( Acquisition.Implicit
                        , Persistence.Persistent
                        ):
       """ Hold persistent references to content """

       def __init__( self, path=_marker ):
           if path is not None:
               self._path = path
           else:
               self._path = _marker

       def edit( self, new_path=_marker ):
           """ Update the reference. """
           if path is not _marker:
               # Am I allowed to touch this object?
               try:
                   obj = self.restrictedTraverse( path )
               except KeyError:
                   raise ObjectReferenceError, \
                         'Invalid path "%s"' % path
           self._path = path

       def getPath( self ):
           """ return the path i use as object reference """
           if self._path == _marker:
               return ''

           return self._path

       def getObject( self ):
         """ Return the object I am pointing to, or None. """
         if self._path == _marker:
             return None

         try:
             obj = self.unrestrictedTraverse( self._path )
         except KeyError:
             raise ObjectReferenceError, \
                   'Cannot find %s' % self_path
         return obj

One variation on this would be to cache the target object in
a volatile attribute following lookup;  I have tried this, and
then ripped it out as the additional complexity seemed worse
than the performance hit of retraversing the path on each
access.

Hope that helps,

Tres.
-- 
===============================================================
Tres Seaver                                tseaver@zope.com
Zope Corporation      "Zope Dealers"       http://www.zope.com