[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