I'm attempting to add new fields to a hosted feature layer if they don't already exist. Currently my code will add the first field in the list (dictionary), but then fails with the message:
Exception: Unable to add feature service layer definition.
Invalid definition for System.Collections.Generic.List`1[ESRI.ArcGIS.SDS.FieldInfo]
Object reference not set to an instance of an object.
(Error Code: 400)
If it's run a second time it does not add the second field and gives the same error. I added the sleep thinking it may help but it doesn't. Any suggestions appreciated. Thanks.
from arcgis.gis import GIS
from arcgis.features import FeatureLayer
import time
username = ''
password = ''
gis = GIS("<url>", username, password, verify_cert=False)
item_id = "<item_id>"
feature_layer_item = gis.content.get(item_id)
feature_layer = FeatureLayer.fromitem(feature_layer_item)
#feature_layer = FeatureLayer.fromitem(feature_url)
fields = feature_layer.properties.fields
existing_field_names = [field.name for field in fields]
new_fields = [
{"name":"newfield1", "alias": "New Field 1","type": "esriFieldTypeInteger"},
{"name":"newfield2", "alias": "New Field 2","type":"esriFieldtypeInteger"},
{"name":"newfield3", "alias": "New Field 3","type":"esriFieldtypeInteger"}
]
fields_to_add_list = [] # this is named 'list' but is actually a dictionary
for new_field in new_fields:
if new_field["name"] not in existing_field_names:
fields_to_add_list.append(new_field)
print(f"Field '{new_field['name']}' will be added.")
else:
print(f"Field '{new_field['name']}' already exists.")
for field_to_add in fields_to_add_list:
print(field_to_add)
feature_layer.manager.add_to_definition({"fields":[field_to_add]})
time.sleep(20)
Solved! Go to Solution.
Hi @JustinWolff,
I was able to reproduce your error and fix it. Add in the full field definitions as per below.
from arcgis.gis import GIS
from arcgis.features import FeatureLayer
username = ''
password = ''
gis = GIS("<url>", username, password, verify_cert=False)
item_id = "<item_id>"
feature_layer_item = gis.content.get(item_id)
feature_layer = FeatureLayer.fromitem(feature_layer_item)
fields = feature_layer.properties.fields
existing_field_names = [field.name for field in fields]
## DEFINE THE FIELDS
new_fields = [
{
"name": "newfield1",
"type": "esriFieldTypeInteger",
"actualType": "int",
"alias": "New Field 1",
"sqlType": "sqlTypeInteger",
"nullable": True,
"editable": True
},
{
"name": "newfield2",
"type": "esriFieldTypeInteger",
"actualType": "int",
"alias": "New Field 2",
"sqlType": "sqlTypeInteger",
"nullable": True,
"editable": True
},
{
"name": "newfield3",
"type": "esriFieldTypeInteger",
"actualType": "int",
"alias": "New Field 3",
"sqlType": "sqlTypeInteger",
"nullable": True,
"editable": True
}
]
## REMOVE ANY FIELDS FROM THE LIST THAT ARE ALREADY IN THE FEATURE LAYER
new_fields = [field_info for field_info in new_fields if field_info["name"] not in existing_field_names]
## ADD THE NEW FIELDS
feature_layer.manager.add_to_definition({"fields":new_fields})
I hope that helps.
All the best,
Glen
Instead of running multiple calls to add_to_definition does doing every field in the fields_to_add_list list work better? I.e.:
feature_layer.manager.add_to_definition({"fields": fields_to_add_list})
That gives the same error, except it doesn't add the first field, when left out of brackets:
feature_layer.manager.add_to_definition({"fields":fields_to_add_list})
And does not create the first field when placed in brackets:
feature_layer.manager.add_to_definition({"fields":[fields_to_add_list]})
but gives a bit different error:
Exception: Unable to add feature service layer definition.
Invalid definition for System.Collections.Generic.List`1[ESRI.ArcGIS.SDS.FieldInfo]
Unable to cast object of type 'System.Object[]' to type 'System.Collections.Generic.IDictionary`2[System.String,System.Object]'.
(Error Code: 400)
and if I use field_to_add outside of any brackets it completes without any errors but doesn't add any fields
feature_layer.manager.add_to_definition({"fields":field_to_add})
with the print statements as they currently are:
Field 'newfield1' will be added.
Field 'newfield2' will be added.
Field 'newfield3' will be added.
[{'name': 'newfield1', 'alias': 'New Field 1', 'type': 'esriFieldTypeInteger'}, {'name': 'newfield2', 'alias': 'New Field 2', 'type': 'esriFieldtypeInteger'}, {'name': 'newfield3', 'alias': 'New Field 3', 'type': 'esriFieldtypeInteger'}]
{'name': 'newfield1', 'alias': 'New Field 1', 'type': 'esriFieldTypeInteger'}
{'name': 'newfield2', 'alias': 'New Field 2', 'type': 'esriFieldtypeInteger'}
{'name': 'newfield3', 'alias': 'New Field 3', 'type': 'esriFieldtypeInteger'}
Am I missing something in the field definition?
Hi @JustinWolff,
I was able to reproduce your error and fix it. Add in the full field definitions as per below.
from arcgis.gis import GIS
from arcgis.features import FeatureLayer
username = ''
password = ''
gis = GIS("<url>", username, password, verify_cert=False)
item_id = "<item_id>"
feature_layer_item = gis.content.get(item_id)
feature_layer = FeatureLayer.fromitem(feature_layer_item)
fields = feature_layer.properties.fields
existing_field_names = [field.name for field in fields]
## DEFINE THE FIELDS
new_fields = [
{
"name": "newfield1",
"type": "esriFieldTypeInteger",
"actualType": "int",
"alias": "New Field 1",
"sqlType": "sqlTypeInteger",
"nullable": True,
"editable": True
},
{
"name": "newfield2",
"type": "esriFieldTypeInteger",
"actualType": "int",
"alias": "New Field 2",
"sqlType": "sqlTypeInteger",
"nullable": True,
"editable": True
},
{
"name": "newfield3",
"type": "esriFieldTypeInteger",
"actualType": "int",
"alias": "New Field 3",
"sqlType": "sqlTypeInteger",
"nullable": True,
"editable": True
}
]
## REMOVE ANY FIELDS FROM THE LIST THAT ARE ALREADY IN THE FEATURE LAYER
new_fields = [field_info for field_info in new_fields if field_info["name"] not in existing_field_names]
## ADD THE NEW FIELDS
feature_layer.manager.add_to_definition({"fields":new_fields})
I hope that helps.
All the best,
Glen
Thanks Glen, that works well. As a follow-on, my test data only has one layer [0]. I'll have other situations where there are multiple sublayers. I'm a bit confused how to use gis.content to return the separate layers. Any help pointing me in the right direction is appreciated. Thanks again.
Hi @JustinWolff,
The below will help. A Feature Service Item (FeatureLayerCollection) has a property called layers that returns a list of FeatureLayer objects for the service. You can iterate over these or access via indexing.
from arcgis import GIS
## acess ArcGIS Online
agol = GIS("home")
## get the Feature Service as an Item object
item = agol.content.get("FS_ITEM_ID")
## print all feature layer
## note: returns a list of FeatureLayer objects
print(item.layers)
## get the index and name for each layer
for index, fl in enumerate(item.layers):
print(index, fl.properties.name)
## access via index
fl = item.layers[1]
print(fl)