[Zope3-Users] Losely or rather, less loosely coupled objects.

Tom Dossis td at yoma.com.au
Thu Mar 9 08:12:15 EST 2006


Max M wrote:
> When doing sites in Zope 2 I often have the need to couple/contain two 
> or more fixed objects.
> 
> When an object needs to have some specific properites and logic, many 
> developers choose to subclass an existing object and change that.
> 
> Eg. a member folder needs to *allways* have a 'contact_info' object. Eg. 
> to keep company policy and to ease skinning.
> 
> That would typically lead to a new folderish content type with contact 
> info properties. Which I think is a really bad pattern.
> 
> 
> Normally in Zope 2 I just give it a fixed id, and then set the 
> folder._reserved_names = ('contact_info',) property on the parent folder.
> 
> This is like folder.contacts_info = ContactInfo()
> 
> But this is a pretty obscure and unknown feature. And site managers 
> cannot use it.
> 
> 
> I have the suspicion that many cases where people are doing Archetype 
> subclasses in Plone they should really use something like that approach 
> instead. It would lead to much simpler maintenance in the long run, with 
> a looser coupling of objects and less repetition of functionality.
> 
> 
> Are there any good patterns for this in Zope 3? It would be really nice 
> to have a standard way of doing it.

Setup the interfaces and class, e.g.

  class IContactInfo(Interface):
    name=schema.TextLine()
    email=schema.TextLine()
    # etc.

  class ContactInfo(Persistent):
    implements(IContactInfo)
    name = FieldProperty['IContactInfo']
    # etc,

  class IContactable(Interface):
    """Marker interface for objects which can have IContactInfo"""

Setup an adapter from  IContactable to IContactInfo.  The ContactInfo 
object is stored as an attribute annotation on the IContactable object.

   def contactInfoAnnotation(ob):
     annotations = IAnnotations(ob)
     info = annotations.get('contact_info')
     if info is None:
       annotations['contact_info'] = info = ContactInfo()
     return info

   provideAdapter(contactInfoAnnotation, [IContactable], IContactInfo)


Now *any object* which implements IContactable (and 
IAttributeAnnotatable) can have ContactInfo.  You access the 
ContactInfo, e.g.

   info = IContactInfo(ob)
   info.name, info.email = u'My Name', u'my at email'
   # etc.

For example, you could even make a file object contactable,...

   classImplements(File, IAttributeAnnotatable)
   classImplements(File, IContactInfo)

   ob = File()
   contact_info = IContactInfo(ob)

You'd more likely use ZCML class implements directives to mark which 
content classes you want to be IContactable.


> It would also be nice if those objects did not show up in the navigation 
> like folder contents. It confuses the users that they cannot delete it. 
> But rather "outside" the normal navigation. Like in a portlet or as a 
> list of actions.

The attribute annotation is not publishable.  You expose it explicitly 
via views.  You can even combine it with the IContactable object in the 
view...

   class MyView(BrowserView):
     __used_for__=IMyObject
     def __init__(self, context, request):
       super(MyView ...
       self.contact_info = IContactInfo(context)
           # or maybe it should be IContactable(IContactInfo(context)) ?

 From your zpt..

   <span tal:replace="context/some_attr"> </span>
   <span tal:replace="view/contact_info/name"> My Name </span>


It's even simpler in a formlib view, because it will automatically adapt 
your context attribute similar to the above...

   class MyForm(form.DisplayForm):
     form_fields=form.Field(
	IMyObject, IContactInfo,
	)

> 
> In Plone that is possible by adding a dot to the id like 
> '.contact_info', but that is a hack. Also there is no reason to have 
> those ugly urls.
> 
> Any pointers, or am I the only one thinking along those lines?

Have a look at Zope3 DublinCore which is done via annotations.

-Tom



More information about the Zope3-users mailing list