3

I'm trying to create a tool that compares two data sets. I do not want the Feature Compare tool. Most of the field names in the two data sets are the same, but not all. I want a way to map one field onto another, so I thought a Field Mappings parameter would be best suited for associating fields together. My expectation was that it would populate similar to how setting the 'Obtained From' sub-parameter to a layer populates the fields list with all available fields from that layer, but that did not happen. I want functionality almost exactly like the Append tool. It populates the 'Field Map (optional)' window with a list of field maps where the user can add input fields.

I've tried working with the tool validator class, in the UpdateParameters method, I've added code to create a FieldMappings object and fill it with FieldMap objects created from the fields in another parameter, but I can't get the window on the tool itself to fill with the FieldMap objects. I've debugged to the point where I know for a fact the FieldMappings object is filled how I want it. I just can't get it to display.

Here is my code for the updateParameters method. param[0] and param[1] are feature layers. param[2] is a field mappings parameter.

def updateParameters(self):
    """Modify the values and properties of parameters before internal
    validation is performed.  This method is called whenever a parameter
    has been changed."""
    if self.params[0].altered:
    self.params[2].value = arcpy.FieldMappings()
    for field in [a for a in arcpy.ListFields(self.params[0].value.dataSource) if not a.required]:
        fm = arcpy.FieldMap()
        fm.addInputField(self.params[0].value.dataSource,field.name)
        self.params[2].value.addFieldMap(fm)
    return

I have also tried this:

def updateParameters(self):
        """Modify the values and properties of parameters before internal
        validation is performed.  This method is called whenever a parameter
        has been changed."""
        if self.params[0].altered:
            fms = arcpy.FieldMappings()
            for field in [a for a in arcpy.ListFields(self.params[0].value.dataSource) if not a.required]:
                fm = arcpy.FieldMap()
                fm.addInputField(self.params[0].value.dataSource,field.name)
                fms.addFieldMap(fm)
            self.params[2].value = fms
        return

I can't find a single post on creating a field mapping parameter in a script similar to the append or spatial join tool.

If the autopopulation is not possible, even getting the FieldMapping parameter to work properly would be an acceptable middle ground. Right now, the 'Add Input Field' never appears when right clicking a field map parameter. If I set the Field Mapping parameter to single value, clicking the window gives 2 options, Add Output Field and Reset. If I add an output field, right clicking that gives more options, Delete, Rename, Merge Rule, and Properties. None of which allow me to add an input field to the field map.

5
  • This may be a bug. I could get it to populate a default mapping by building the FieldMappings separately and then setting the parameter value as a string (self.params[2].value = fms.exportToString()). This takes a looooong time in a Python toolbox (minutes). Even then, it doesn't function properly (you can't add new fields and setup input fields to them). Commented May 31, 2019 at 18:51
  • 1
    Field mappings are the absoloute worst to create in arcpy. My advice is to do the operation in Arcmap with your two layers and then right click in the results window and script the action. Look at the field mapping it made and copy that into your script. It's dumb and it feels like a hacky work around to what should be a simple problem but this has saved me hours of headache.
    – Brian W.
    Commented May 31, 2019 at 19:10
  • @EvilGenius you method worked to get the base field map, but, like your issue, I can't add input fields or additional FieldMaps to the list... Kind of defeats the purpose of the FieldMappings object.. Commented Jun 3, 2019 at 14:17
  • @BrianW. I'm writing a script for use by other people, so having things work all-in-one is kind of a necessity. I'll keep that in mind for future needs of field mappings though! Commented Jun 3, 2019 at 14:19
  • @PatrickTighe did you ever get this to work? I pretty much have the same issue
    – user20224
    Commented Mar 31, 2020 at 18:31

1 Answer 1

1

I was trying to solve a similar problem and I've found the way to do it here. Field Mapping's parameter value is an empty object and before adding a field to the list, the list should be created. This can be done by adding an empty field mapping in updateParameters() like this:

import arcpy


class Toolbox(object):
    def __init__(self):
        """Define the toolbox (the name of the toolbox is the name of the
        .pyt file)."""
        self.label = "Toolbox"
        self.alias = ""

        # List of tool classes associated with this toolbox
        self.tools = [Tool]


class Tool(object):
    def __init__(self):
        """Define the tool (tool name is the name of the class)."""
        self.label = "Tool"
        self.description = ""
        self.canRunInBackground = False

    def getParameterInfo(self):
        """Define parameter definitions"""
        param0 = arcpy.Parameter(
            displayName="Input Table",
            name="in_table",
            datatype="GPTableView",
            parameterType="Required",
            direction="Input")

        param1 = arcpy.Parameter(
            displayName="Field Mappings",
            name="in_fieldmappings",
            datatype="GPFieldMapping",
            parameterType="Required",
            direction="Input")

        params = [param0, param1]
        return params

    def isLicensed(self):
        """Set whether tool is licensed to execute."""
        return True

    def updateParameters(self, parameters):
        """Modify the values and properties of parameters before internal
        validation is performed.  This method is called whenever a parameter
        has been changed."""
        # if the input table view is changed
        if parameters[0].altered:
            inputTable = parameters[0].value

            # add a temporary item to the field mappings list
            # can be removed later by calling parameters[1].value.removeFieldMap(0)
            parameters[1].value = str('Empty')

            # add table fields
            parameters[1].value.addTable(inputTable)

            # or iterate through the fields...
            # for field in arcpy.ListFields(inputTable):
            #     fm = arcpy.FieldMap()
            #     fm.addInputField(inputTable, field.name)
            #     parameters[1].value.addFieldMap(fm)
        return

    def updateMessages(self, parameters):
        """Modify the messages created by internal validation for each tool
        parameter.  This method is called after internal validation."""
        return

    def execute(self, parameters, messages):
        """The source code of the tool."""
        return

You can now load a table view and field mappings will be populated automatically. You can then add more fields or edit the initial ones.

Please check hasbeenValidated property also: if parameters[0].altered and not parameters[0].hasbeenValidated: if You want to get field(s) deleted or modified by end user from tool UI...

2
  • I realize this is a little old, but I am having trouble getting your example to work. I keep getting the error AttributeError: 'Parameter' object has no attribute 'addTable'
    – user20224
    Commented Mar 30, 2020 at 17:54
  • Hm, this is strange. How do you define your parameters? I've updated my answer and added the entire toolbox class so you can check getParameterInfo() method. Commented Apr 1, 2020 at 9:37

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.