Trouble with DeleteField_

1056
7
09-13-2021 02:11 PM
StuartJordan
New Contributor II

Apologies in advance for asking for help on something very basic.

I've got a gdb containing many many layers.  I need to grab just a couple of fields out of each layer and merge them all together into a single layer.  Fields across layers have different names but the same content.  I'm wanting to do it all in a Python notebook because I need to document every little step.  I'm coming from R and this would be a breeze there.  But struggling to learn basic data management in arcpy.

Anyway, I'm failing in the first step just trying to delete fields in a newly created layer.  My guess: I'm supposed to be working with a 'Table' and instead I'm working with a 'Feature Layer'...y'all can tell me whether that's right.

Here's what I've done so far.  First, I set my workspace to the gdb containing all the layers.  Then I'm creating the feature layer in-memory that will be the ultimate output layer:

arcpy.management.MakeFeatureLayer('Brighton_Zoning', 'out_layer')

('Brighton_Zoning' is one of the layers in the gdb).

Now, there's only four fields I'm ultimately going to want in out_layer: ZONE_ABBR, ZONING, MUNICIPALITY and SHAPE.  But what I just did above is put ALL the fields from Brighton_Zoning (there are a lot) into 'out_layer'.  So next step is to delete all those excess fields.  So let's just try this one at a time...we'll start with a field called 'OBJECTID_12'.  Here we go

arcpy.DeleteField_management("out_layer", "OBJECTID_12")

And...that throws an error:

StuartJordan_0-1631567088058.png

I've tried this with several of the other field names.  Also tried it with a list of the field names.  Also tried it with single quotes instead of double quotes.

Clearly, there's something very very very basic about ArcGIS I do not understand.

I need to read a book about it!  Oh wait, I did!  I read this: https://www.amazon.com/Python-Scripting-ArcGIS-Paul-Zandbergen/dp/1589484991/ref=sr_1_4?dchild=1&key...

That's the recommend book from esri on arcpy.  But here's the thing...it teaches you how to modify data row-by-row (i.e. arcpy.da with cursors).  It doesn't teach how  to manage the structure of entire tables/features/datasets.

So if anyone knows of a book that teaches how to data munge in arcpy (i.e. manipulate fields, table joins, row binds, etc. etc.)  please point me that way!

And if you can explain the error above, please do!

Thanks!

0 Kudos
7 Replies
DanPatterson
MVP Esteemed Contributor

Delete Field (Data Management)—ArcGIS Pro | Documentation

perhaps your first parameter.... the featureclass need some help, but providing the full path or setting the workspace like in the help topic

arcpy.env.workspace = "C:/data"


... sort of retired...
0 Kudos
DanLee
by Esri Regular Contributor
Esri Regular Contributor

Your OBJECTID_12 may be a required field, which is permanent and cannot be deleted, as the error message description says.

https://pro.arcgis.com/en/pro-app/latest/tool-reference/tool-errors-and-warnings/001001-010000/tool-...

0 Kudos
DanPatterson
MVP Esteemed Contributor

Perhaps the table has a temporary join then, which would make the id field required and unable to delete the other fields as well


... sort of retired...
0 Kudos
JohannesLindner
MVP Frequent Contributor

Sorry if you already know this, but your post doesn't seem like it:

There is a difference between a "feature class" and a "feature layer". The class ist the physical object stored in the database. The layer is (very roughly speaking) a representation of that class, which allows you to do certain things that you can't do directly with the class (e.g. selection, symbology).

So when you make a feature layer of Brighton_Zoning, this is NOT a copy. If you succeed deleting a field from the layer, it will be deleted from the underlying feature class, too!

 

As to your problem:

I find fiddling with the field mapping for the Append tool too cumbersome, so I'd do it with the Cursors:

https://pro.arcgis.com/de/pro-app/latest/arcpy/get-started/data-access-using-cursors.htm

 

# create the output feature class
# creating it in memory makes everthing faster, you just have to copy it to a gdb afterwards
out_fc = arcpy.management.CreateFeatureclass("memory", "out_fc", "POLYGON")

# add the output fields
arcpy.management.AddField(out_fc, "TextField", "TEXT")
arcpy.management.AddField(out_fc, "IntegerField", "LONG")

# create a list of feature classes to append, their fields, and sql queries determining the entries that are to be copied
# fields have to be in the same order for each fc
# geometry type has to be the same for each fc
append_fcs = [
    {"path": "path/of/fc1", "fields": ["SHAPE@", "TEXT", "INTEGER"], "sql": None},
    {"path": "path/of/fc2", "fields": ["SHAPE@", "Text_Field", "Integer_Field"], "sql": "Text_Field LIKE '%abc%'"},
    ]

# start the insert cursor
with arcpy.da.InsertCursor(out_fc, ["SHAPE@", "TextField", "IntegerField"]) as i_cursor:
    # loop though append_fcs and copy each row to out_fc
    for fc in append_fcs:
        with arcpy.da.SearchCursor(fc["path"], fc["fields"], fc["sql"]) as s_cursor:
            for row in s_cursor:
                i_cursor.insertRow(row)

Have a great day!
Johannes
StuartJordan
New Contributor II

Ah...this is great!  You're reply lead to some searching that lead to this stackexchange thread...and now things are starting to feel more clear!  Thank you!

0 Kudos
StuartJordan
New Contributor II

Thank you all!

I will try re-specifying the path (thank you, @DanPatterson !), look into whether one of the fields I'm trying to delete is "required" (thank you, @DanLee !) or bound by a temporary join (thank you, @DanPatterson ! )

@JohannesLindner  your answer is pitched at exactly the level I need.  At this point, part of what I find so challenging about ArcGIS is that I'm mystified by the object model.  Any pointers to where I can find a primer on data types in ArcGIS, and what exactly determines which ones (when in-memory) are mutable in what conditions?  Oh yea...and also, which operations are the ones that write to disk and which only modify objects in-memory?  Programming without this basic knowledge of the data and i/o models is scary!  I need a primer!

Thanks!

-Stu

0 Kudos
StuartJordan
New Contributor II

OK, folks.  Thanks to your help, I've learned enough to solve the problem.  I'll put the code below, but first a couple of comments, which hopefully will help anyone who is trying to solve similar problems who comes across this thread:

First, @JohannesLindner 's point is critical for any of this: the problem I'm trying to solve involves working with featureclasses, not featurelayers!  So my opening sentence from my query is completely mis-stated.  It reads "I've got a gdb containing many many layers...".  In fact the situation is that I've got a gdb containing many many feature classes!

Second, my initial query describes what I needed to do as "merge" these classes.  There is a  tool called merge in arcpy, and it probably could be used to do what I needed done.  But it is documented in a way that (at least for me), made it hard to distinguish some of the critical steps involved. The tool that I was able to make work is called "append".  It has the advantage that the example arcpy code in its documentation is solving EXACTLY THE SAME PROBLEM I was trying to solve! 

Finally, a key challenge in the problem I was trying to solve is to write the code in a way that allows for carefully keeping track and displaying how the fields from multiple classes that are hold the same data have different names.  What you'll see in the code below is that I've tried to solve this problem by creating a python class that will hold the critical data about how fields are named for each arcgis class.  I then create an instance of this class corresponding to each featureclass, and loop through that, pulling the relevant data from each one as needed.  The key python trick in making this all work is liberal use of `getattr()` inside the loops! The structure of the code is exactly the same as in the code example for "append", except that what I've written iterate through lists of the relevant featureclasses and fields.

What you'll see below is the code for just two featureclasses.  In reality, I'm have to append data from thirty!...but the real code is too long to post here!

#set the workspace to the gdb holding the features for each municipality
arcpy.env.workspace = ###OMITTED###
#create the receiving featureclass
cw_fc = arcpy.CreateFeatureclass_management(
    out_path=###OMITTED###,
    out_name="countywide.shp",
    geometry_type="POLYGON",
    spatial_reference=###OMITTED###
)
arcpy.management.AddField(
    in_table=cw_fc,
    field_name="MUNICIPALI",
    field_type="TEXT",
    field_alias="MUNICIPALI"
)
arcpy.management.AddField(
    in_table=cw_fc,
    field_name="ZONE_ABBR",
    field_type="TEXT",
    field_alias="ZONE_ABBR"
)
arcpy.management.AddField(
    in_table=cw_fc,
    field_name="ZONING",
    field_type="TEXT",
    field_alias="ZONING"
)
#define the class of objects that will hold the data we need for each successive input class
class InLayer():
    def __init__(self, layer, muniNM, zone_abbrNM, zoningNM) :
        self.layer = layer
        self.MUNICIPALI = muniNM
        self.ZONE_ABBR = zone_abbrNM
        self.ZONING = zoningNM
#enter the data (only 2 classes here...the real one will contain 30)
inlayers = [
    InLayer(
        layer =         'Brighton_Zoning',
        muniNM =        "MUNICIPALITY",
        zone_abbrNM =   "ZONE_ABBR",
        zoningNM =      "ZONING"
    ),
    InLayer(
        layer =         'Brockport_Zoning',
        muniNM =        "MUNICIPALITY",
        zone_abbrNM =   "ZONE_ABBR",
        zoningNM =      "ZONING"
    )
]
#create fieldmappings object for appending this layer
fieldmappings = arcpy.FieldMappings()
#add the target shp to the field mappings to set the schema
fieldmappings.addTable(cw_fc)

#Loop through the fields that need to be mapped
for fld in ["MUNICIPALI", "ZONE_ABBR", "ZONING"] :
    #create a fieldmap
    fldMap = arcpy.FieldMap()    
    #loop through the layers populate the input side of the fieldmap
    for lyr in inlayers :
        #add the input field for the current layer
        fldMap.addInputField(lyr.layer, getattr(lyr,fld))
    #set the output field name
    oname = fldMap.outputField
    oname.name, oname.aliasName, oname.type = fld, fld, "TEXT"
    fldMap.outputField = oname
    #add the outputfield to the fieldmappings object
    fieldmappings.addFieldMap(fldMap)
        
#It should now be the case that fieldmappings is populated with all of the relevant inputs, matched to the appropriate outputs!        
#so...append!
arcpy.Append_management(
    inputs=[getattr(lyr, "layer") for lyr in inlayers],
    target=###OMITTED###,
    schema_type="NO_TEST",
    field_mapping=fieldmappings,
    subtype=""
)

 

0 Kudos