Package: collective.relationhelpers
Helpers to manage, create, export, inspect and rebuild relations in PloneTo learn more about relations readhttps://training.plone.org/5/mastering-plone/relations.htmlFeaturesDealing with relations on individual objectsConvenience methods:relations(obj, attribute=None, as_dict=False)Get related objects.unrestricted_relations(obj, attribute=None, as_dict=False)Get related objects without permission checks.backrelations(obj, attribute=None, as_dict=False)Get objects with a relation to this object.unrestricted_backrelations(obj, attribute=None, as_dict=False)Get objects with a relation to this object without permission checks.relation(obj, attribute)Get related object. This is only valid if the attribute is the name of a relationChoice field on the object.unrestricted_relation(obj, attribute)Get related object without permission checks. See relationbackrelation(obj, attribute)Get relating object. This only makes sense when one item has a relation of this type to the obj.
One example is parent -> child where only one parent can exist.unrestricted_backrelation(obj, attribute)Get relating object without permission checks. See backrelationrelapi.link_objects(source, target, relationship)Link objects: Create a relation between two objects using the specified relationship.
From the parameterrelationshipthe method will find out what kind of relationship you want to create (RelationChoice, RelationList) by inspecting the schema-field on the source-object.
The method also works for linkintegrity-relations and relations between working-copies.Example: To use the default-behaviorplone.relateditemsuse the field-namerelatedItemsas relationship:relapi.link_objects(obj, anotherobj, 'relatedItems').Main method to get all kinds of relations:relapi.get_relations(obj, attribute=None, backrels=False, restricted=True, as_dict=False)Get a list of incoming or outgoing relation for a specific content object.If you pass a attribute you only get relations of that type. This is the same as the fieldname on the source-object and thefrom_attributeon a RelationValue. You can also pass a list if attributes to get relations of certain types.By default the result is a list of objects. If you set as_dict=True it will return a dict with the names of the relations as keys and lists of objects as values.Dealing with all relations at onceEspecially during migrations (e.g. between Archetypes and Dexteriy or from Python 2 to 3) you need to deal with all relations at once.
For example the relation-catalog and the intid-catalog could hold references to broken or removes objects.First import the api:from collective.relationhelpers import api as relapirelapi.rebuild_relations()Rebuild all relations using the same code as the form@@rebuild-relationsrelapi.get_all_relations()Get all relations as a list of dicts.relapi.export_relations()Export all relations as a json fileall_relations.jsonin you buildout directory.relapi.store_relations()Export all relations and store them as a annotation on the portalIAnnotations(portal)['ALL_REFERENCES'].relapi.cleanup_intids()Purge all RelationValues and all references to broken objects from the IntId catalog.relapi.get_relations_stats()Log information on all existing relationsrelapi.restore_relations(all_relations=None)Recreate relations from a annotation on the portal or a list of dicts (e.g. restored from the json-file created byexport_relations).
This works fine for all kinds of relations, RelationList- or RelationChoice-fields (including the default field “Related Items”) as well as for linkintegrity-relations and relations between working-copies.Rebuild all relationsThere is a formhttp://localhost:8080/Plone/@@rebuild-relationsthat rebuilds all relations.It exports all valid reations from the relation-catalog, purges the relation-catalog (and the intid-catalog) and restores all valid relations.Inspect relationsThere is a controlpanelhttp://localhost:8080/Plone/@@inspect-relationsthat allows you to inspect all relations in your site:Dealing with Archetypes-RelationsThis package does not support Archetypes but it can be of great help to migrate relations form Archetypes to Dexterity.
Here are two upgrade-steps.The first stores all relations (AT and DX) as a annotation on the portal). Run that in Plone 4 or 5 if you still have AT content.:def store_relations(context=None):
    from plone.app.contenttypes.migration.utils import store_references
    portal = api.portal.get()
    store_references(portal)The second restores them. Run it after you migrated all your content to Dexterity.:# Map References used with of Archetypes (Plone 4) to the ones used in Plone 5 with Dexterity
RELATIONSHIP_FIELD_MAPPING = {
    'Working Copy Relation': 'iterate-working-copy',
    'relatesTo': 'relatedItems',
}

IGNORE = [
    'translationOf',  # LinguaPlone relation
]

def restore_relations(context=None):
    portal = api.portal.get()
    all_stored_relations = IAnnotations(portal)['ALL_REFERENCES']
    log.info('Loaded {0} relations to restore'.format(
        len(all_stored_relations))
    )
    all_fixed_relations = []
    for rel in all_stored_relations:
        if rel['relationship'] in ignore:
            continue
        # plone.app.contenttypes exports references with 'relationship' but relationshelpers
        # expects 'from_attribute' which is what zc.relation uses.
        # Also some relationships have changed their name
        rel['from_attribute'] = RELATIONSHIP_FIELD_MAPPING.get(rel['relationship'], rel['relationship'])
        all_fixed_relations.append(rel)
    all_fixed_relations = sorted(all_fixed_relations, key=itemgetter('from_uuid', 'from_attribute'))
    relapi.restore_relations(all_relations=all_fixed_relations)InstallationInstall collective.relationhelpers by adding it to your buildout:[buildout]

...

eggs =
    collective.relationhelpersand then runningbin/buildout.ContributeIssue Tracker:https://github.com/collective/collective.relationhelpers/issuesSource Code:https://github.com/collective/collective.relationhelpersSupportIf you are having issues, please create a ticket.LicenseThe project is licensed under the GPLv2.ContributorsPhilip Bauer,bauer@starzel.deChangelog1.6 (2021-10-11)Don’t registery the control panel views on Plone 6 as they have been added to Plone core. [fredvd]1.5 (2021-04-19)Handle case where value of relationchoice field is None instead of empty list.
[pbauer]Fix inspecting relations that are not explicitly broken but raise a IntIdMissingError (#7).
[fulv]1.4 (2021-02-19)Never return None for a relation where source or target is missing.
[pbauer]Use title_or_id to allow missing title.
[pbauer]1.3 (2021-02-12)Rename views to be the same as they will be in Plone 6.
Seehttps://github.com/plone/Products.CMFPlone/issues/3231[pbauer]Add some tests.
[pbauer]Add support for broken relations.
[pbauer]Drop unrequired access to fti.
[pbauer]Fixed query for existing relations inlink_objects.
[maurits]1.2 (2021-01-13)Add view /@@relationinfo to inspect relations.
[pbauer]1.1 (2020-12-15)Log duplicates when dropping them during restore_relations.
[pbauer]Work around a problem with z3c.relationfield.event.updateRelations that prevented relations from behaviors that are registered with a marker-interface from being registered.
[pbauer]Add progress-logger when restoring relations
[pbauer]1.0 (2020-10-02)Add functions to clear and rebuild intids.
[krissik]Add example for migrating Archetypes relations to Dexterity.
[pbauer]Defer calling modified until the end to speed up importing relations a lot if you use many relationlists.
[pbauer]Keep original order of relations while purging duplicates.
[pbauer]1.0a2 (2020-09-15)Api change: Return objects by default, optionally return a dict by relationname
[pbauer]Api change: Check view-permissions by default
[pbauer]Api change: Add convenience-methods relations, backrelations, unrestricted_relations and unrestricted_backrelations
[pbauer]Api change: Add convenience-method relation for relationChoice that only returns one object, not a list
[pbauer]Api change: Rename parameter backref to backrel
[pbauer]Api change: Allow to query for multiple reations
[pbauer]1.0a1 (2020-05-29)Initial release.
[pbauer]