Select to view content in your preferred language

Using Cursors on Versioned Feature Class Layers

1967
8
Jump to solution
12-15-2022 08:57 AM
RogerDunnGIS
Frequent Contributor

I am using ArcGIS Pro 2.9.3.  I am a seasoned Python programmer, but need help meeting the following requirements:

  • Environment: A Python toolbox in an ArcGIS Pro project, opened by a user who is manually running the tools
  • A map in an ArcGIS Pro project contains several layers that come from feature classes in an enterprise geodatabase feature dataset which is registered as versioned (traditional)
  • Read the values of some unselected records in various layers in the map.  I will use arcpy.da.SearchCursor with a where clause.
  • Update some values of some unselected records in various layers in the map.  I will use arcpy.da.UpdateCursor, again with a where clause.

I cannot figure out what combination of strings that arcpy.da methods need, given my environment.  I will only know which map layer the user wants to work with at run-time.  Its data source will be a feature class in a versioned dataset on an enterprise  geodatabase.  Given the variables below, which values will make arcpy.da methods happy, so I can Search, Update, Insert, and Delete rows from a layer, regardless of selection, and still be able to see those changes within the map?

projectName = "CURRENT"
project = arcpy.mp.ArcGISProject(projectName)
mapName = "Map"
map_ = next(filter(lambda m: m.name == mapName, project.listMaps()))
grpLayerName = "theGroup"
mapLayerName = "theLayer"
mapLayerLongName = "\\".join((grpLayerName, mapLayerName))
layer = next(filter(lambda lyr: lyr.longName == mapLayerLongName, map_.listLayers()))
dbName = "obfuscated"
schemaName = "adminUser"
datasetName = "bigDataSet"
datasetLongName = ".".join((dbName, schemaName, datasetName))
featClassName = "bigFeatureClass"
featClassLongName = ".".join((dbName, schemaName, featClassName))
sdePath = r"C:\Users\Myself\Documents\ArcGIS\Projects\BigProject"
sdeFileName = "enterprise.sde"
sdeFilePath = os.path.join(sdePath, sdeFileName)
desc = arcpy.da.Describe(layer)
# desc.catalogPath equals os.path.join(sdeFilePath, datasetLongName, featClassLongName)
# desc.path equals os.path.join(sdeFilePath, datasetLongName)
# SOMETIMES, however, an .sde file in a Temp path is seen, rather than the
# one in my project!
# desc.aliasName, baseName, file, and name all equal featClassLongName

arcpy.da.Editor(workspace argument?)
arcpy.da.UpdateCursor(in_table argument?)

 

I have tried so many combinations of strings for the workspace and in_table arguments, it's not even funny.  I get errors like:

  • "Objects in this class cannot be updated outside an edit session"
  • "cannot open workspace"
  • "insufficient permissions" (even though the user editing is the owner)
  • Or I succeed with the edit, but the map can't see it until I go to Contents > right-click the database connection and select Refresh.  I'm okay refreshing the map or the table view, but I don't want to have to ask the user to refresh the connection

Any help, ideas, or guidance would be appreciated.

0 Kudos
1 Solution

Accepted Solutions
NicoleV
Occasional Contributor

I was having some similar errors working off sde data a while ago but I determined that my editing session inputs were backwards so I would make sure that those are correct from the editor documentation. That should get rid of the "Objects in this class cannot be updated outside an edit session" and "cannot open workspace" errors. Not sure about the insufficient permissions though.

Also I believe that with versioned enterprise data another user will not be able to see edits you make to your version until there has been a rec and post unless they have access to your version. In that case I still believe they would have to refresh the connection due to the edits not being made during their own session. However, if you are making the edits within the python window and you cannot see the results until refreshing the connection, I'm not sure how to fix that bug.

 

This is a snippet of my code for a similar type of project that it seems you are attempting with the edit session inputs I used for sde data. Let me know if this helps or if I missed the mark entirely.

workspace = r"enterprise.sde"

edit = arcpy.da.Editor(workspace)

edit.startEditing(False, False)

edit.startOperation()

with arcpy.da.UpdateCursor(layername, "field") as cursor:
    for row in cursor:
        argument

edit.stopOperation()

edit.stopEditing(True)

Nicole

View solution in original post

0 Kudos
8 Replies
BlakeTerhune
MVP Regular Contributor

I had these same problems. Upgrading to ArcGIS Pro 3.0.3 was the fix for me.

0 Kudos
NicoleV
Occasional Contributor

I was having some similar errors working off sde data a while ago but I determined that my editing session inputs were backwards so I would make sure that those are correct from the editor documentation. That should get rid of the "Objects in this class cannot be updated outside an edit session" and "cannot open workspace" errors. Not sure about the insufficient permissions though.

Also I believe that with versioned enterprise data another user will not be able to see edits you make to your version until there has been a rec and post unless they have access to your version. In that case I still believe they would have to refresh the connection due to the edits not being made during their own session. However, if you are making the edits within the python window and you cannot see the results until refreshing the connection, I'm not sure how to fix that bug.

 

This is a snippet of my code for a similar type of project that it seems you are attempting with the edit session inputs I used for sde data. Let me know if this helps or if I missed the mark entirely.

workspace = r"enterprise.sde"

edit = arcpy.da.Editor(workspace)

edit.startEditing(False, False)

edit.startOperation()

with arcpy.da.UpdateCursor(layername, "field") as cursor:
    for row in cursor:
        argument

edit.stopOperation()

edit.stopEditing(True)

Nicole

0 Kudos
RogerDunnGIS
Frequent Contributor

Okay, but if layername is the name of a layer as it appears in the table of contents, it will have a selection, which I need to ignore for the various Cursors.

0 Kudos
RogerDunnGIS
Frequent Contributor

I spoke too soon.  Here is the code that worked for me, based on what you said.  Given a layer in a map, I can do this:

tableName = desc["catalogPath"]
fields = ["OBJECTID", "COMMENT"]
query = "OBJECTID IN ({0})".format(", ".join((str(objId) for objId in objectIds)))
eddy = arcpy.da.Editor(sdeFileName)
eddy.startEditing(True, True)
eddy.startOperation()
with arcpy.da.UpdateCursor(tableName, fields, query) as uCursor:
    for row in uCursor:
        row[1] = "Test Comment"
        uCursor.updateRow(row)
del uCursor
eddy.stopOperation()
eddy.stopEditing(True)
del eddy

 

This will modify a row that is not part of a selection!  Love it! 

BlakeTerhune
MVP Regular Contributor

Glad you found a solution! I recommend testing your error handling here. If there's an error in your edit session, it may not close properly without a try/except statement or using a with statement.

0 Kudos
RogerDunnGIS
Frequent Contributor

My try blocks generally look like this:

 

eddy = arcpy.da.Editor(myWorkspace)
try:
    eddy.startEditing(False, False) # Values depend on workspace
    try:
        # Operation 1
        eddy.startOperation()
        try:
            with arcpy.da.UpdateCursor(in_table1, fields1, query1) as uCursor:
                for row in uCursor:
                    # do something
                    uCursor.updateRow(row)
            eddy.stopOperation()
        except:
            eddy.abortOperation()
            raise
        finally:
            del uCursor

        # Operation 2
        eddy.startOperation()
        try:
            with arcpy.da.UpdateCursor(in_table2, fields2, query2) as uCursor:
                for row in uCursor:
                    # do something
                    uCursor.updateRow(row)
            eddy.stopOperation()
        except:
            eddy.abortOperation()
            raise
        finally:
            del uCursor
        eddy.stopEditing(True)
    except:
        eddy.stopEditing(False)
        raise
finally:
    del eddy

 

Updated to show how I now work my try/except/finally blocks.  If anything goes wrong, the entire transaction is rewound and the database is left as it was, which makes this work well.

mejohnson22
Occasional Contributor

Hi Roger, did this edit the selected features in the default version? Or does it focus on only the version in the project?

0 Kudos
RogerDunnGIS
Frequent Contributor

That's a great question.  I don't work with any other versions than DEFAULT and that's because I don't know how to make versions easy.  In ArcGIS Desktop you had to modify your database connection (.sde) file and specify which version you were using.  There was also a version toolbar.  I think the version that is affected is the version used by the layer you're editing (if you specify a layer name for UpdateCursor) or the version tied to the .sde file if you're using a full-path to the feature class.  It's worth testing for sure.

Most of what I know I found out by reading, asking, trying, and making lots of mistakes.  If I was in your shoes, knowing this operation is important but volatile, I would set up some stuff.  I'd create a test dataset, a few test feature classes, with a few fields, make the dataset versioned (and which kind?), and try these changes in your code.  But a word of warning in determining if something worked or not: I have seen many cases where the change performed by Python isn't visible in the map, isn't visible after a refresh, isn't visible even after refreshing the version in the map, and isn't visible till after I close out Pro and reopen it.  I don't know why.

0 Kudos