Detect Relationship in Feature Class or Table

7996
8
08-09-2012 10:23 AM
StephanieSnider
Occasional Contributor III
ArcGIS Desktop 10.0

I have a script that copies feature classes and tables from one workspace to another on a schedule.  It is a backup script.  My problem is that when I try to copy a feature class that has a relationship class with a table, the copy fails - because the table that is associated with the feature class by a relationship also tries to be copied and that table already exists.  I would like to have a way to detect if a feature class is in a relationship, and what other featureclasses or tables it shares that relationship with.  I tried the following on a feature class that I know has a relationship, but the result came back empty.

>>> desc = arcpy.Describe(r'Database Connections\BWQP_NDEP24.sde\BWQP.AssessedStreams2010')
>>> desc
<geoprocessing describe data object object at 0x1E829440>
>>> print "%-22s %s" % ("RelationshipClassNames:", desc.relationshipClassNames)
RelationshipClassNames: []


I'm able to list the relationship classes, but I can't describe them enough to see the feature classes and tables associated with them.
>>> rc_list = [c.name for c in arcpy.Describe(workspace).children if c.datatype == "RelationshipClass"]
>>> rc_list
[u'BWQP.Lakes_to_TMDL', u'BWQP.Lakes_to_Waterbody', u'BWQP.Rivers_to_TMDL', u'BWQP.Rivers_to_Waterbody', u'BWQP.AssessedLakes_to_IR10', u'BWQP.AssessedStreams_to_IR10']


I also read that there is a bug NIM048192 with python in that it does not have access to the key relationship class properties. 

Help!!
Tags (2)
0 Kudos
8 Replies
StephanieSnider
Occasional Contributor III
Answer from ESRI tech support (Stephanie W.)

import arcpy
from arcpy import env
workspace = env.workspace = r'your_workspace'
def detectRelationship():
     rc_list = [c.name for c in arcpy.Describe(workspace).children if c.datatype == "RelationshipClass"]
     for rc in rc_list:
         rc_path = workspace + "\\" + rc
         des_rc = arcpy.Describe(rc_path)
         origin = des_rc.originClassNames
         destination = des_rc.destinationClassNames
         print "Relationship Class: %s \n Origin: %s \n Desintation: %s" %(rc, origin, destination)         
detectRelationship()


Result is like this:
Relationship Class: NDEP.WaterDiv_to_Owners_NDWR2012 
 Origin: [u'NDEP.WaterDiversions_NDWR2012'] 
 Desintation: [u'NDEP.WaterDiv_Owners_NDWR2012']


Yeah!!!
0 Kudos
RobertMartin2
Occasional Contributor II
Stephanie,

Were you able to get anywhere with your backup script? I'm having the same problem right now - nothing seems to be working. I'm curious how you used the code snippet from Esri to avoid the duplicates.
0 Kudos
StephanieSnider
Occasional Contributor III
Robert,
The above code only detects the FIRST relationship class on a feature class.  For example, if a feature class has two relationship classes on two different tables, the code only reports the first relationship and the associated table.  Totally ignores the second relationship and table.  Ahhh!!!   Because of this flaw, I wasn�??t able to use the relationship detector code in my backup script.  I discovered that instead of using the Copy function (which brings over related objects), I could use feature class to feature class conversion and table to table conservation which only copies the feature class or table and not the related objects.  The overwrite function works with the two conversion functions but not the copy function (NIM046461). 

Here's a copy of a my script.  [ATTACH=CONFIG]21818[/ATTACH]

It doesn't work with feature datasets yet.  Two of my SDE accounts throw errors when I try to copy feature datasets and I haven't figure out why yet - saving for later when I have time to bang my head against the wall.  🙂

Good Luck!!!
0 Kudos
StephanieSnider
Occasional Contributor III
So ignore my previous post saying the script doesn't find all the relationships on the feature class.  This function finds the relationship classes and then tells you the feature classes associated with it.  A relationship class can only have two related objects.  I think I was trying to get it to work the opposite way.  Not sure why I didn't see this before, but I wanted to make sure I corrected my mistake on the forum.  The detectRelationship code works but returns the information by print.  I'll need to work on the code to get it to combine and return lists (relationship class, origin, destination) that can be called later and used in other functions.

Stephanie
0 Kudos
WilliamCraft
MVP Regular Contributor
Hello,

I tried the suggested code above but am getting the following error:

Traceback (most recent call last):
  File "C:/Users/00223562/Desktop/test.py", line 17, in <module>
    detectRelationship()
  File "C:/Users/00223562/Desktop/test.py", line 10, in detectRelationship
    rc_list = [c.name for c in gp.Describe(workspace).children if c.datatype == "RelationshipClass"]
TypeError: 'geoprocessing list object' object is not iterable

My environment consists of ArcGIS Desktop 9.3.1 SP2 and Python 2.5.  Thanks very much in advance for any help!
0 Kudos
JamesMcGinley
New Contributor II
This should do what you need.  It will avoid copying an object which was previously copied because it takes part in a relationship class with another object.

import arcgisscripting

gp = arcgisscripting.create(9.3)


SDE_WORKSPACE = "Database Connections\\sde931.sde"
FGDB_WORKSPACE = "c:/931t.gdb"


#CHECK IF EXISTS
def objExists(o):
 gp.workspace = FGDB_WORKSPACE
 if gp.Exists(o):
  print "  " + o + " exists"
  return True
 else:
  #print "  " + o + " does not exist"
  return False


# START COPY FEATURE CLASSES
gp.workspace = SDE_WORKSPACE
fcList = gp.ListFeatureClasses()
for sde_fc in fcList:
    #print "Start copy " + sde_fc
    if not objExists(sde_fc.split(".")[1]):
        gp.workspace = SDE_WORKSPACE
        gp.Copy_management(sde_fc, "C:\\931t.gdb\\" + sde_fc.split(".")[1])
    #print "End copy " + sde_fc


# START COPY TABLES
gp.workspace = SDE_WORKSPACE
tList = gp.ListTables()
for sde_t in tList:
    #print "Start copy " + sde_t
    if not objExists(sde_t.split(".")[1]):
        gp.workspace = SDE_WORKSPACE
        gp.Copy_management(sde_t, "C:\\931t.gdb\\" + sde_t.split(".")[1])
    #print "End copy " + sde_t
0 Kudos
brettangel
Occasional Contributor II
Any ideas on how to do this?  Originally, I thought I could use the relationshipClassNames from the GDB Table properties, but I only get empty value back (i.e. []).  Anybody know why this does not work or what I'm doing incorrectly?  Not much to this and the example is pulled straight from ESRI help documents.

desc = arcpy.Describe("myTable")
print desc.relationshipClassNames


I found this thread with this code

import arcpy
from arcpy import env
workspace = env.workspace = r'your_workspace'
def detectRelationship():
     rc_list = [c.name for c in arcpy.Describe(workspace).children if c.datatype == "RelationshipClass"]
     for rc in rc_list:
         rc_path = workspace + "\\" + rc
         des_rc = arcpy.Describe(rc_path)
         origin = des_rc.originClassNames
         destination = des_rc.destinationClassNames
         print "Relationship Class: %s \n Origin: %s \n Desintation: %s" %(rc, origin, destination)         
detectRelationship()

, but I don't need to search the entire DB for all relationships.  Only a specific table.  I could use this code to ID the table and relationships I want, but would prefer to use the relationshipClassNames from GDB Table to reduce the amount of code.

Any help would be much appreciated.

Oops, thought I hit new thread.  Going to leave this here just in case.
MicahBabinski
Occasional Contributor III

Here's some code I've been using. It might help.

def GetWorkspace(inputFeatureClass):
    """
    Returns the workspace which contains the input feature class
    """
    path = arcpy.Describe(inputFeatureClass).path
    if arcpy.Describe(path).dataType in ("Workspace", "Folder"):
        workspace = path
    else:
        workspace = arcpy.Describe(path).path

    return workspace


def hasRelatedTables(inputTable):
    """
    Returns true if the input table participates in a relationship class
    """
    if arcpy.Describe(inputTable).relationshipClassNames != []:
        return True
    else:
        return False


def GetRelatedTableInfo(inputTable):
    """
    Returns a dictionary of relationship class info
    """
    relTableInfo = {}
    workspace = GetWorkspace(inputTable)
    if hasRelatedTables(inputTable):
        relClasses = arcpy.Describe(inputTable).relationshipClassNames
        for rc in relClasses:
            relClassProps = arcpy.Describe(os.path.join(workspace, rc))
            if os.path.join(workspace, relClassProps.originClassNames[0]) == inputTable:
                isTopLevel = True
            else:
                isTopLevel = False
            relTableInfo[relClasses.index(rc)] = {"IsTopLevel": isTopLevel,
                                                  "RelClassName": rc,
                                                  "ParentTable": os.path.join(workspace, relClassProps.originClassNames[0]),
                                                  "ChildTable": os.path.join(workspace, relClassProps.destinationClassNames[0]),
                                                  "PrimaryKey": [k[0] for k in relClassProps.originClassKeys if k[1] == "OriginPrimary"][0],
                                                  "ForeignKey": [k[0] for k in relClassProps.originClassKeys if k[1] == "OriginForeign"][0],
                                                  "IsAttachment": relClassProps.isAttachmentRelationship,
                                                  "Cardinality": relClassProps.cardinality}

        return relTableInfo

    else:
        return {}


def ListRelatedTables(inputTable, excludeAttachments = False):
    """
    """
    if not hasRelatedTables(inputTable):
        return None
    
    relatedTables = []
    relTableInfo = GetRelatedTableInfo(inputTable)
    if excludeAttachments:
        for item in relTableInfo:
            if not relTableInfo[item]["IsAttachment"]:
                relatedTables.append(relTableInfo[item]["ParentTable"])
                relatedTables.append(relTableInfo[item]["ChildTable"])
    else:
        for item in relTableInfo:
            relatedTables.append(relTableInfo[item]["ParentTable"])
            relatedTables.append(relTableInfo[item]["ChildTable"])

    relatedTables = list(set(relatedTables))
    if inputTable in relatedTables:
        relatedTables.remove(inputTable)

    return relatedTables
‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

For list related tables function I only wanted non-attachment relationships.

Hope this helps!

Micah

0 Kudos