Select to view content in your preferred language

CreateRelationshipClass with MANY_TO_MANY - how?

289
5
3 weeks ago
nadja
by
Frequent Contributor

I'm trying to create several Relationship Classes with the cardinalities 1:1 and n:n. The below script works fine with my 1:1 relationship classes. It runs also for the n:n cardinalities, creates some relationship classes, but I can't access its values with the explore tool as with the 1:1 relationship classes. Why? What's wrong? 

for index, row in df.iterrows():
    arcpy.management.CreateRelationshipClass(origin_table = row["origin_table"],
                                            destination_table = row["destination_table"],
                                            out_relationship_class = row["out_relationship_class"],
                                            relationship_type = row["relationship_type"],
                                            forward_label = row["forward_label"],
                                            backward_label = row["backward_label"],
                                            message_direction = row["message_direction"],
                                            cardinality = row["cardinality"],
                                            attributed = row["attributed"],
                                            origin_primary_key = row["origin_primary_key"],
                                            origin_foreign_key = row["origin_foreign_key"],
                                            destination_primary_key = row["destination_primary_key"],
                                            destination_foreign_key = row["destination_foreign_key"])

 

with my table:

origin_tabledestination_tableout_relationship_classrelationship_typeforward_labelbackward_labelmessage_directioncardinalityattributedorigin_primary_keyorigin_foreign_keydestination_primary_keydestination_foreign_key
mgdm_polygone_polygonmgdm_polygone_regelpolygon_regelSIMPLERegelpolygonNONEMANY_TO_MANYNONERegelRegelRegelCode#
0 Kudos
5 Replies
TonyAlmeida
MVP Regular Contributor

ArcGIS creates intermediate relationship table that stores the pairs of related objects. Unlike 1:1 relationships, you cannot directly access related records through the Explore tool without this intermediate table.

Try something like this.

import arcpy
import os

def create_relationship_with_intermediate_check(row):
    """Create relationship class and handle intermediate table for n:n"""
    
    # Create the relationship class
    arcpy.management.CreateRelationshipClass(
        origin_table=row["origin_table"],
        destination_table=row["destination_table"], 
        out_relationship_class=row["out_relationship_class"],
        relationship_type=row["relationship_type"],
        forward_label=row["forward_label"],
        backward_label=row["backward_label"],
        message_direction=row["message_direction"],
        cardinality=row["cardinality"],
        attributed=row["attributed"],
        origin_primary_key=row["origin_primary_key"],
        origin_foreign_key=row["origin_foreign_key"],
        destination_primary_key=row["destination_primary_key"],
        destination_foreign_key=row["destination_foreign_key"]
    )
    
    # Handle n:n specific setup
    if row["cardinality"].upper() == "MANY_TO_MANY":
        handle_many_to_many_relationship(row)

def handle_many_to_many_relationship(row):
    """Locate and verify the intermediate table for an n:n relationship class."""
    
    relationship_class = row["out_relationship_class"]

    print(f"n:n Relationship created: {relationship_class}")

    # Describe the relationship class
    desc = arcpy.Describe(relationship_class)

    # ArcGIS tells us exactly which table is the join table
    rel_table_path = desc.relationshipTable

    if not rel_table_path:
        print("No intermediate relationship table found (Describe returned None).")
        return

    print(f"Intermediate table located: {rel_table_path}")

    # Verify it exists
    if arcpy.Exists(rel_table_path):
        print("Intermediate relationship table verified")

        # Inspect fields
        rel_desc = arcpy.Describe(rel_table_path)
        fields = [f.name for f in rel_desc.fields]
        print(f"Relationship table fields: {fields}")
    else:
        print("Relationship table path does not exist in the workspace.")

 

0 Kudos
nadja
by
Frequent Contributor

Thank you @TonyAlmeida. If I understand your code correctly, this assumes that there already is an intermediate relationship table, correct? how do you create such a table? I found https://pro.arcgis.com/en/pro-app/3.4/tool-reference/data-management/table-to-relationship-class.htm, but this gp tool too assumes there is already such an intermediate table. 

0 Kudos
TonyAlmeida
MVP Regular Contributor

Yes, the code assumes the intermediate table already exists.
But ArcGIS automatically creates the intermediate table for you when BOTH of the following are true:

cardinality = "MANY_TO_MANY"

attributed = "NONE" (i.e., NOT attributed)

0 Kudos
nadja
by
Frequent Contributor

I get an error if i try to access the relationshipTable from a RelationshipClass using 

print(rc.relationshipTable)
AttributeError: DescribeData: Method relationshipTable does not exist

 

0 Kudos
TonyAlmeida
MVP Regular Contributor

Try this,

Set the parameter values to:

cardinality = "MANY_TO_MANY"
attributed = "NONE"

and

row dictionary contains:
row["cardinality"] = "MANY_TO_MANY"
row["attributed"] = "NONE"

import arcpy
import os

def create_relationship_with_intermediate_check(row):
    """Create relationship class and handle intermediate table for n:n"""

    # Should auto-create the join table
    if row["cardinality"].upper() == "MANY_TO_MANY":
        row["attributed"] = "NONE"  # REQUIRED for system-generated table

    # Create the relationship class
    arcpy.management.CreateRelationshipClass(
        origin_table=row["origin_table"],
        destination_table=row["destination_table"],
        out_relationship_class=row["out_relationship_class"],
        relationship_type=row["relationship_type"],
        forward_label=row["forward_label"],
        backward_label=row["backward_label"],
        message_direction=row["message_direction"],
        cardinality=row["cardinality"],
        attributed=row["attributed"],
        origin_primary_key=row["origin_primary_key"],
        origin_foreign_key=row["origin_foreign_key"],
        destination_primary_key=row["destination_primary_key"],
        destination_foreign_key=row["destination_foreign_key"]
    )

    # Handle M:N after creation
    if row["cardinality"].upper() == "MANY_TO_MANY":
        handle_many_to_many_relationship(row)

def handle_many_to_many_relationship(row):
    """Locate and verify the intermediate table for an n:n (non-attributed) relationship."""

    relationship_class = row["out_relationship_class"]
    print(f"n:n Relationship created: {relationship_class}")

    desc = arcpy.Describe(relationship_class)

    # SAFE ATTRIBUTE CHECK
    if hasattr(desc, "relationshipTable"):
        rel_table_path = desc.relationshipTable
    else:
        print("No system-generated intermediate table exists (attributed=ATTRIBUTED or invalid RC).")
        return

    if not rel_table_path:
        print("Describe returned a blank relationshipTable property.")
        return

    print(f"Intermediate table located: {rel_table_path}")

    if arcpy.Exists(rel_table_path):
        print("Intermediate table verified.")

        # List fields
        fields = [f.name for f in arcpy.ListFields(rel_table_path)]
        print(f"Relationship table fields: {fields}")
    else:
        print("Relationship table does not exist in the workspace.")

 

0 Kudos