Getting back the getBackReferences

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.

So I came with: http://docs.plone.org/external/plone.app.dexterity/docs/advanced/references.html

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?

my best,
alessandro.

Hi!

Applying the plone.app.referenceablebehavior.referenceable.IReferenceable behavior should expose context.getBrefs() from https://github.com/plone/plone.app.referenceablebehavior/blob/master/plone/app/referenceablebehavior/referenceable.py#L92 .
AFAIK Dexterity adapts the context automatically for you.

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.

alessandro.

Just to make sure: both the source and target objects' FTI specify the IReferenceable behavior, right?

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).

sincerely, thanks for your interest
alessandro.

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

(c) by @jensens

1 Like

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

my best,
alessandro.

Hi everyone, I have the same problem here, even using the IReferenceable behavior the methods don't show up into p.a.contenttypes Image type.

Can someone please guide me on how to enable back these AT methods at DX objetcs?