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'[EMAIL PROTECTED]'
  # 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

_______________________________________________
Zope3-users mailing list
Zope3-users@zope.org
http://mail.zope.org/mailman/listinfo/zope3-users

Reply via email to