I wrote a code to modify field aliases and there are multiple layers, some with great many columns, which require alias modification.
Below is the main part of the code which is supposed to be responsible for alias modificaiton. The issue is that after the process is run the aliases in on layers tables have not changed and I can't see why that is!
Layers is a list of layer names in the gdb. The new_alias is to replace the current alias of the columns.
with open(logpath, 'a') as cur:
for layer in layers:
layer = os.path.join(gdb, f"{fds}/{layer}")
print(f'Layer Name: {layer}', end=f'\n{"="*100}\n')
cur.write(f'\nLayer Name: {layer}\n{"="*100}\n')
columns = arcpy.ListFields(layer)
for field in columns:
if not field.required and len(field.name) > 20:
new_alias = field.aliasName.replace("_", " ").replace('PCT', '%').replace('plus', '+')
cur.write(f'Pre-processed Alias: {field.aliasName}\n')
arcpy.management.AlterField(in_table=layer
,field=field.name
,new_field_alias=new_alias)
print(f'Alias: {new_alias}\nField: {field.name}\n{"-" * 50}')
cur.write(f'Alias: {new_alias}\nField: {field.name}\n{"-" * 50}\n')
Would you please help me on why this is not working. I am using ArcGISPro?
Thanks in advance!
Solved! Go to Solution.
If your log is showing that the alias is updated, it probably is actually updated. If you have an arcpro instance open, those changes won't be seen until you re open the project though.
if it's not the offending line might be:
if not field.required and len(field.name) > 20:
you're checking field.name but you probably mean to be checking field.aliasName
I did just re-write your code to check this and this works if you restart so here's a class object that takes a gdb path, a logpath, and an optional callable for string re-writes:
import arcpy
import os
class AliasUpdater:
def __init__(self, gdb: os.PathLike, logpath: os.PathLike=None, update_rule: callable=None) -> None:
self.gdb = gdb
if not arcpy.Exists(self.gdb):
raise FileNotFoundError(f'{self.gdb} does not exist')
# Set workspace and get all datasets
arcpy.env.workspace = self.gdb
self.fds = arcpy.ListDatasets()
# Initialize log
self.log = []
self.logpath = logpath
# Get All Features and Tables
self.features: list = arcpy.ListFeatureClasses()
self.features.extend(arcpy.ListTables())
for ds in self.fds:
self.features.extend([f"{ds}\\{fc}" for fc in arcpy.ListFeatureClasses(feature_dataset=ds)])
# Accept a callable to update the alias
self.update_rule: callable = update_rule if update_rule else self._alias_rule
def update_all_aliases(self):
# Update Aliases for all features
for feature in self.features:
self._update_feature_alias(feature)
if self.logpath:
self._log()
def _update_feature_alias(self, feature: os.PathLike):
# Print Feature Name and Log
self._print(f'Feature Name: {feature}\n{"="*100}')
for field in arcpy.ListFields(feature):
# Skip if field is required (Add additional rules here if needed)
if field.required: continue
# Update Alias
self._update_field_alias(feature, field)
def _update_field_alias(self, feature: os.PathLike, field: arcpy.Field):
# Run current alias through alias rule
new_alias = self.update_rule(field.aliasName)
# If new alias is the same as the current alias, return
if new_alias == field.aliasName: return
# Update alias
self._print(f'\tUpdating {field.name} alias from {field.aliasName} to {new_alias}')
arcpy.management.AlterField(feature, field.name, new_field_alias=new_alias)
def _alias_rule(self, alias: str) -> str:
""" Change this to do any string processing you want on the alias """
return alias.replace("_", " ").replace('PCT', '%').replace('plus', '+')
def _print(self, message: str):
"""Prints message and appends to log"""
print(message)
self.log.append(message)
def _log(self):
"""Writes log to logpath if provided"""
with open(self.logpath, 'a') as f:
f.write('\n'.join(self.log))
def main():
def alt_rule(alias: str) -> str:
return alias.upper()
gdb = r'<path to gdb>'
logpath = r'<path to log>'
updater = AliasUpdater(gdb, logpath=logpath)
updater.update_all_aliases()
alt_updater = AliasUpdater(gdb, logpath=logpath, update_rule=alt_rule)
alt_updater.update_all_aliases()
if __name__ == '__main__':
main()
I also want to offer the code that I wrote and it is seemingly guaranteed to work, hopefully for any kind of dataset, and no matter the size. Here I don't bother logging the layer name and field names and aliases. This code takes a very long time to run as I am running it from the python window on ArcGIS. And the code does not work outside ArcGIS environment in terms of changing the display name (alias) on the tables!
As for the other solutions demonstrated above, I have tested them but they are not guaranteed to fix the displayed aliases although I checked the layers individually for their aliases and they had been modified, but the table still showed unmodified aliases. Not sure exactly why it worked the first time for 4 layers I tested it on, then it stopped working!!!!!!?
Now to the code:
import arcgis, arcpy, os
arcpy.env.overwriteOutput = True
layers = ["poverty_6574", "poverty_5564", "poverty_75plus", "poverty_15plus"]
for layer in layers:
print(layer, end='\n\n')
columns = arcpy.ListFields(layer)
print("Changing the field alias by replacing \"_\" with \" \" as well as other necessary changes")
for field in columns:
if not field.required:
new_alias = field.aliasName.replace("_", " ").replace('PCT', '%').replace('++','').replace('+','').replace('15','15+').replace('75',' 75+')
arcpy.management.AlterField(layer, field.name, new_field_alias=new_alias)
print(field.aliasName)
Try altering your script to output the new field alias names only, then run the alter field tool by hand for a few of them using that output. Does it fail silently again, or do you get an error?
ExecuteError: Failed to execute. Parameters are not valid.
ERROR 000735: Field Name: Value is required
Failed to execute (AlterField).
So, I commented out the field=field.name line and it is causing an error! field name is required based on the doc.
If your log is showing that the alias is updated, it probably is actually updated. If you have an arcpro instance open, those changes won't be seen until you re open the project though.
if it's not the offending line might be:
if not field.required and len(field.name) > 20:
you're checking field.name but you probably mean to be checking field.aliasName
I did just re-write your code to check this and this works if you restart so here's a class object that takes a gdb path, a logpath, and an optional callable for string re-writes:
import arcpy
import os
class AliasUpdater:
def __init__(self, gdb: os.PathLike, logpath: os.PathLike=None, update_rule: callable=None) -> None:
self.gdb = gdb
if not arcpy.Exists(self.gdb):
raise FileNotFoundError(f'{self.gdb} does not exist')
# Set workspace and get all datasets
arcpy.env.workspace = self.gdb
self.fds = arcpy.ListDatasets()
# Initialize log
self.log = []
self.logpath = logpath
# Get All Features and Tables
self.features: list = arcpy.ListFeatureClasses()
self.features.extend(arcpy.ListTables())
for ds in self.fds:
self.features.extend([f"{ds}\\{fc}" for fc in arcpy.ListFeatureClasses(feature_dataset=ds)])
# Accept a callable to update the alias
self.update_rule: callable = update_rule if update_rule else self._alias_rule
def update_all_aliases(self):
# Update Aliases for all features
for feature in self.features:
self._update_feature_alias(feature)
if self.logpath:
self._log()
def _update_feature_alias(self, feature: os.PathLike):
# Print Feature Name and Log
self._print(f'Feature Name: {feature}\n{"="*100}')
for field in arcpy.ListFields(feature):
# Skip if field is required (Add additional rules here if needed)
if field.required: continue
# Update Alias
self._update_field_alias(feature, field)
def _update_field_alias(self, feature: os.PathLike, field: arcpy.Field):
# Run current alias through alias rule
new_alias = self.update_rule(field.aliasName)
# If new alias is the same as the current alias, return
if new_alias == field.aliasName: return
# Update alias
self._print(f'\tUpdating {field.name} alias from {field.aliasName} to {new_alias}')
arcpy.management.AlterField(feature, field.name, new_field_alias=new_alias)
def _alias_rule(self, alias: str) -> str:
""" Change this to do any string processing you want on the alias """
return alias.replace("_", " ").replace('PCT', '%').replace('plus', '+')
def _print(self, message: str):
"""Prints message and appends to log"""
print(message)
self.log.append(message)
def _log(self):
"""Writes log to logpath if provided"""
with open(self.logpath, 'a') as f:
f.write('\n'.join(self.log))
def main():
def alt_rule(alias: str) -> str:
return alias.upper()
gdb = r'<path to gdb>'
logpath = r'<path to log>'
updater = AliasUpdater(gdb, logpath=logpath)
updater.update_all_aliases()
alt_updater = AliasUpdater(gdb, logpath=logpath, update_rule=alt_rule)
alt_updater.update_all_aliases()
if __name__ == '__main__':
main()
Thanks for the code. I am testing it right now on the data I am working with. I am running it outside ArcGIS from Jupyter Notebook. Hopefully it works , otherwise I will run it from ArcGIS GUI to see if aliases change.
I will update you on it! 🙂
This code worked only when I used it within ArcGIS environment. Outside ArcGIS environment this code fails.
It is worth noting that due to overhead, running the code inside the ArcGIS environment is significantly slower than running it outside ArcGIS.
Here's a version that uses da.Walk to get the full workspace path for each object. Also added a check to make sure the item you're updating the alias for is a FeatureClass or Table (skips rasters and topologies):
import arcpy
import os
class AliasUpdater:
def __init__(self, gdb: os.PathLike, logpath: os.PathLike=None, update_rule: callable=None) -> None:
self.gdb = gdb
if not arcpy.Exists(self.gdb):
raise FileNotFoundError(f'{self.gdb} does not exist')
self.features = {}
for root, _, features in arcpy.da.Walk(self.gdb):
self.features.update({f: os.path.join(root, f) for f in features})
# Initialize log
self.log = []
self.logpath = logpath
# Accept a callable to update the alias
self.update_rule: callable = update_rule if update_rule else self._alias_rule
def update_all_aliases(self):
# Update Aliases for all features
for feature in self.features.items():
if arcpy.Describe(feature[1]).dataType not in ['FeatureClass', 'Table']: continue
self._update_feature_alias(feature)
if self.logpath:
self._log()
def _update_feature_alias(self, feature: tuple[str, os.PathLike]):
# Print Feature Name and Log
name, path = feature
self._print(f'Feature Name: {name}\n{"="*100}')
for field in arcpy.ListFields(path):
# Skip if field is required (Add additional rules here if needed)
if field.required: continue
# Update Alias
self._update_field_alias(feature, field)
def _update_field_alias(self, feature: tuple[str, os.PathLike], field: arcpy.Field):
_, path = feature
# Run current alias through alias rule
new_alias = self.update_rule(field.aliasName)
# If new alias is the same as the current alias, return
if new_alias == field.aliasName: self._print(f'\t{field.name} alias is up to date'); return
# Update alias
self._print(f'\tUpdating {field.name} alias from {field.aliasName} to {new_alias}')
arcpy.management.AlterField(path, field.name, new_field_alias=new_alias)
def _alias_rule(self, alias: str) -> str:
""" Change this to do any string processing you want on the alias """
return alias.replace("_", " ").replace('PCT', '%').replace('plus', '+')
def _print(self, message: str):
"""Prints message and appends to log"""
print(message)
self.log.append(message)
def _log(self):
"""Writes log to logpath if provided"""
with open(self.logpath, 'a') as f:
f.write('\n'.join(self.log))
Not sure if this is supposed to work outside ArcGIS, but anyhow I tried it outside ArcGIS and it doesn't seem to be changing the aliases on the table after I restart ArcGIS and check the tables although I did not go through the full cycle of the shapes on gdb as I had to interrupt it because I noticed there is an unwanted line of code which I need to modify to ```if len(field.name) > 20```:
# If new alias is the same as the current alias, return if new_alias == field.aliasName: self._print(f'\t{field.name} alias is up to date'); return
# If the length of the field name is less than 20, return
if len(field.aliasName) < 20: self._print(f'\t{field.name} alias is up to date'); return
I also want to offer the code that I wrote and it is seemingly guaranteed to work, hopefully for any kind of dataset, and no matter the size. Here I don't bother logging the layer name and field names and aliases. This code takes a very long time to run as I am running it from the python window on ArcGIS. And the code does not work outside ArcGIS environment in terms of changing the display name (alias) on the tables!
As for the other solutions demonstrated above, I have tested them but they are not guaranteed to fix the displayed aliases although I checked the layers individually for their aliases and they had been modified, but the table still showed unmodified aliases. Not sure exactly why it worked the first time for 4 layers I tested it on, then it stopped working!!!!!!?
Now to the code:
import arcgis, arcpy, os
arcpy.env.overwriteOutput = True
layers = ["poverty_6574", "poverty_5564", "poverty_75plus", "poverty_15plus"]
for layer in layers:
print(layer, end='\n\n')
columns = arcpy.ListFields(layer)
print("Changing the field alias by replacing \"_\" with \" \" as well as other necessary changes")
for field in columns:
if not field.required:
new_alias = field.aliasName.replace("_", " ").replace('PCT', '%').replace('++','').replace('+','').replace('15','15+').replace('75',' 75+')
arcpy.management.AlterField(layer, field.name, new_field_alias=new_alias)
print(field.aliasName)