Ok... perhaps I'm a bit late... but (after 15 years of Archetypes) I finally landed on Dexterity... I kudos myself.
Now....
I thought that .getBackReferences was something recurring in so many usecases to be implemented as core functionality... but it is not so.
I tried to follow:
but this implementation make sense if there are some archetype somewhere... It is not my case.
and I had to write a behavior to cover this. Fine.
The problem is that, even I enable this behavior (by fti) on all dx-content type I need, also I must adapt the context every time to get this method available on that context: i.e. IMyBehavior(obj).getBackReferences(..)
Am I right? Or is there some sort of zcml registration/directive I missed, that can enables a behavior method directly on the objects?
This happens when you extend the schema... or when you use a behavior that implements only a marker interface. In these cases, Dexterity seems to atapts the context as you say.
But when I apply a behavior on a contenty type to provide some other methods to the context, I have to adapt the manually... IMHO very ugly. Even in your example, I activated the behavior IReferenceable to the content type, but the context was left without getBRefs implementation.
Yes, exactly. Plone 5 and 4 makes no difference. The context is not adapted automatically.
(Consider that in Plone5 there is no uid_catalog and it is not possible to use plone.app.referenceablebehavior.referenceable.IReferenceable behavior).
At a recent project we are using references using z3c.relationfield. Our "related topics" behavior looks like:
# -*- coding: utf-8 -*-
from my.project import _
from plone.app.z3cform.widget import RelatedItemsFieldWidget
from plone.autoform.directives import order_after
from plone.autoform.directives import widget
from plone.autoform.interfaces import IFormFieldProvider
from plone.supermodel import model
from z3c.relationfield.schema import RelationChoice
from z3c.relationfield.schema import RelationList
from zope.interface import provider
@provider(IFormFieldProvider)
class IRelatedTopics(model.Schema):
"""Behavior providing fields for one or more related topics.
"""
widget(
'related_topics',
RelatedItemsFieldWidget,
orderable=True,
)
related_topics = RelationList(
title=_(
u'label_related_items',
default=u'Related Topics'
),
description=_(
u'description_related_items',
default=u'Select one or more documents related to this document.'
),
default=[],
value_type=RelationChoice(
title=_(u'label_related_items', default=u'Related Topics'),
vocabulary='plone.app.vocabularies.Catalog',
),
required=False,
)
order_after(related_topics='*')
And this is our view code, which gets you all direct references and backreferences at once:
# -*- coding: utf-8 -*-
from Acquisition import aq_inner
from plone.memoize.view import memoize
from Products.Five import BrowserView
from zc.relation.interfaces import ICatalog
from zope.component import getUtility
from zope.intid.interfaces import IIntIds
from zope.security import checkPermission
class RelatedTopicsView(BrowserView):
@property
@memoize
def _relations(self):
catalog = getUtility(ICatalog)
intids = getUtility(IIntIds)
relations = catalog.findRelations(
dict(
to_id=intids.getId(aq_inner(self.context)),
from_attribute='related_topics'
)
)
relations = list(relations)
return relations
@property
@memoize
def items(self):
already_set = []
result = []
# 1) direct references on top, sorted as given by widget.
for refvalue in self.context.related_topics:
if not refvalue.to_object:
# TODO: should not happen. something waky with the import?
continue
already_set.append(refvalue.to_id)
result.append(self._record(refvalue.to_object))
# 2) back references
intids = getUtility(IIntIds)
for rel in self._relations:
if rel.from_id in already_set:
continue
obj = intids.queryObject(rel.from_id)
if obj is not None and checkPermission('zope2.View', obj):
result.append(self._record(obj))
return result
So, it looks it is as I said. There is no core method to handle back references like archetype before and it is not so straightforward to add a behavior to make context inerhit that method. What a pity.
Thanks for your code! It is really useful to me: I have a context referenced by multiple other objects with different relations so it seems to fit very well my use cases