32. Relations#
You can model relationships between content items by placing them in a hierarchy (for example a (folderish) page speakers containing the (folderish) speakers and within each speaker the talks) or by linking them to each other in blocks. But where would you then store a talk that two speakers give together?
Relations allow developers to model relationships between objects without using links or a hierarchy.
The behavior plone.app.relationfield.behavior.IRelatedItems
provides the field Related Items in the section Categorization.
That field simply says a
is somehow related to b
.
By using custom relations you can model your data in a much more meaningful way.
Check out the code at the relevant tags!
Code for the beginning of this chapter:
# frontend
git checkout sponsors
# backend
git checkout user_generated_content
Code for the end of this chapter:
# frontend
git checkout relations
# backend
git checkout relations
More info in The code for the training
32.1. Creating and configuring relations in a schema#
Relate to one item only with RelationChoice
.
1from z3c.relationfield.schema import RelationChoice
2
3
4 speaker = RelationChoice(
5 title="Speaker",
6 description="The speaker of the talk",
7 vocabulary="plone.app.vocabularies.Catalog",
8 required=False
9 )
Relate to multiple items with RelationList
.
1from z3c.relationfield.schema import RelationChoice
2from z3c.relationfield.schema import RelationList
3
4
5 speaker = RelationList(
6 title="Speaker",
7 description="Speakers of the talk",
8 value_type=RelationChoice(
9 vocabulary="plone.app.vocabularies.Catalog",
10 ),
11 required=False,
12 default=[]
13 )
See also
Controlling what to relate to#
The vocabulary controls which content instances can be related to from the field.
1 speaker = RelationList(
2 title="Speaker",
3 description="Speakers of the talk",
4 value_type=RelationChoice(
5 vocabulary="ploneconf.speakers"
6 ),
7 required=False,
8 default=[]
9 )
We want to relate to content instances of type 'speaker'. So we define a vocabulary of speakers.
src/ploneconf/site/vocabularies/configure.zcml
1 <utility
2 name="ploneconf.speakers"
3 component="ploneconf.site.vocabularies.speaker.SpeakerVocabularyFactory"
4 />
src/ploneconf/site/vocabularies/speaker.py
1from plone.app.vocabularies.catalog import StaticCatalogVocabulary
2from zope.interface import provider
3from zope.schema.interfaces import IVocabularyFactory
4
5
6@provider(IVocabularyFactory)
7def SpeakerVocabularyFactory(context=None):
8 return StaticCatalogVocabulary(
9 {
10 "portal_type": ["speaker"],
11 "review_state": "published",
12 "sort_on": "sortable_title",
13 }
14 )
32.2. The widget#
The widget allows the editor to edit the relations.
The default and only widget by now in Volto is the select
widget.
It opens the tree of content to be selected by the editor.
On saving the talk, the selection is validated according the vocabulary.
For a more sophisticated widget see the explanation on how to write a custom widget in Forms and widgets.
32.4. Inspecting relations#
In Plone 6 Volto you can inspect all relations and inverse relations in your site using the control panel relations
http://localhost:3000/controlpanel/relations.
You can even edit the relations.
You can find the inverse relations of a content instance via the menu item Links and references
32.5. Programming with relations#
Since Plone 6 plone.api
has methods to create, read, and delete relations and inverse relations.
1from plone import api
2
3portal = api.portal.get()
4source = portal.schedule["workflows-made-easy"]
5target = portal.speakers["urs-herbst"]
6api.relation.create(source=source, target=target, relationship="speaker")
1from plone import api
2
3api.relation.get(source=portal.schedule["workflows-made-easy"])
4api.relation.get(relationship="speaker")
5api.relation.get(target=portal.speakers["urs-herbst"])
List all relations of name "speaker":
>>> for rel in api.relation.get(relationship="speaker"): rel.from_object, rel.to_object, rel.from_attribute
...
(<Talk at /Plone/schedule/talkli>, <Speaker at /Plone/speakers/urs-herbst>, 'speaker')
(<Talk at /Plone/schedule/advanced-relations>, <Speaker at /Plone/speakers/katja-i-e-suss>, 'speaker')
See the chapter Relations of the docs for plone.api
for more details.
Plone 5.2 and older#
In older Plone versions you can use collective.relationhelpers to create and read relations and inverse relations in a very similar way.
32.6. Exercise 1#
Add a content type speaker and modify the content type talk to relate to speakers. Write an upgrade step for the change of the field 'speaker'.
The code can be found in backend add-on ploneconf.site
at tag relations
.
32.7. Exercise 2#
The speaker is now a relation on talk. Available on the TalkView is a subset of attributes of the speaker. How would you achieve to show the GitHub handle of the speaker? It is by now not included in the available attributes.