Select to view content in your preferred language

Aliases are not changing

161
8
Jump to solution
Thursday
Seyed-MahdySadraddini
New Contributor III

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!

0 Kudos
1 Solution

Accepted Solutions
HaydenWelch
Occasional Contributor

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()

 

 

 

 

View solution in original post

8 Replies
DavidSolari
Occasional Contributor III

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?

0 Kudos
Seyed-MahdySadraddini
New Contributor III
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.

0 Kudos
HaydenWelch
Occasional Contributor

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()

 

 

 

 

Seyed-MahdySadraddini
New Contributor III

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! 🙂

0 Kudos
Seyed-MahdySadraddini
New Contributor III

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.

0 Kudos
HaydenWelch
Occasional Contributor

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))

 

0 Kudos
Seyed-MahdySadraddini
New Contributor III

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

 

0 Kudos
Seyed-MahdySadraddini
New Contributor III

# 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

0 Kudos