References
-----------

**How to work with references between content objects**

References are a way to maintain links between content that remain valid
even if one or both of the linked items are moved or renamed.

Under the hood, Dexterity’s reference system uses `five.intid`_, a Zope
2 integration layer for `zope.intid`_, to give each content item a unique
integer id. These are the basis for relationships maintained with the
`zc.relationship`_ package, which in turn is accessed via an API
provided by `z3c.relationfield`_, integrated into Zope 2 with
`plone.app.relationfield`_. For most purposes, you need only to worry
about the ``z3c.relationfield`` API, which provides methods for finding
source and target objects for references and searching the relationship
catalog.

References are most commonly used in form fields with a selection or
content browser widget. Dexterity comes with a standard widget in
`plone.formwidget.contenttree`_ configured for the ``RelationList`` and
``RelationChoice`` fields from ``z3c.relationfield``.

To illustrate the use of references, we will allow the user to create a
link between a ``Session`` and its ``Presenter``. Since Dexterity already
ships with and installs ``plone.formwidget.contenttree`` and
``z3c.relationfield``, we do not need to add any further setup code, and
we can use the field directly in ``session.py``::

    ...

    from z3c.relationfield.schema import RelationChoice
    from plone.formwidget.contenttree import ObjPathSourceBinder
    ...

    from example.conference.presenter import IPresenter

    class ISession(form.Schema):
        """A conference session. Sessions are managed inside Programs.
        """
        ...
            
        presenter = RelationChoice(
                title=_(u"Presenter"),
                source=ObjPathSourceBinder(object_provides=IPresenter.__identifier__),
                required=False,
            )

To allow multiple items to be selected, we could have used a
``RelationList`` like::

    relatedItems = RelationList(
            title=u"Related Items",
            default=[],
            value_type=RelationChoice(title=_(u"Related"),
                                      source=ObjPathSourceBinder()),
            required=False,
        )

The ``ObjPathSourceBinder`` class is an ``IContextSourceBinder`` that returns
a vocabulary with content objects as values, object titles as term
titles and object paths as tokens.

You can pass keyword arguments to the constructor for
``ObjPathSourceBinder()`` to restrict the selectable objects. Here, we
demand that the object must provide the ``IPresenter`` interface. The
syntax is the same as that used in a catalog search, except that only
simple values and lists are allowed (e.g. you can’t use a dict to
specify a range or values for a field index).

If you want to restrict the folders and other content shown in the
content browser, you can pass a dictionary with catalog search
parameters (and here, any valid catalog query will do) as the first
non-keyword argument (``navigation_tree_query``) to the
``ObjPathSourceBinder()`` constructor.

If you want to use a different widget, you can use the same source (or a
custom source that has content objects as values) with something like
the autocomplete widget. The following line added to the interface will
make the presenter selection similar to the ``organizer`` selection widget
we showed in the previous section::

    form.widget(presenter=AutocompleteFieldWidget)

Once the user has created some relationships, the value stored in the
relation field is a ``RelationValue`` object. This provides various
attributes, including:

- ``from_object``, the object from which the relationship is made;
- ``to_object``, the object to which the relationship is made;
- ``from_id`` and ``to_id``, the integer ids of the source and target;
- ``from_path`` and ``to_path``, the path of the source and target.

The ``isBroken()`` method can be used to determine if the relationship is
broken. This normally happens if the target object is deleted.

To display the relationship on our form, we can either use a display
widget on a ``DisplayForm``, or use this API to find the object and
display it. We’ll do the latter in ``session_templates/view.pt``:

.. code-block:: html

    <div tal:condition="context/presenter">
        <label i18n:translate="presenter">Presenter:</label>
        <span tal:content="context/presenter/to_object/Title | nothing" />
    </div>

.. _five.intid: http://pypi.python.org/pypi/five.intid
.. _zope.intid: http://pypi.python.org/pypi/zope.intid
.. _zc.relationship: http://pypi.python.org/pypi/zc.relationship
.. _z3c.relationfield: http://pypi.python.org/pypi/z3c.relationfield
.. _plone.app.relationfield: http://pypi.python.org/pypi/plone.app.relationfield
.. _plone.formwidget.contenttree: http://pypi.python.org/pypi/plone.formwidget.contenttree
