Add Layer/Table Definition Query in ArcGIS Pro Based on Selection with ArcPy

You have made a selection based on attributes or location in ArcGIS Pro (or via a script or tool) and you want to set a definition query that only shows the selected records. Let’s walk through making that tool in ArcGIS Pro with ArcPy.

Interested in learning ArcPy? Check out this course!

We start with importing the ArcPy module

import arcpy

We want three user input parameters; in_table as a Feature Layer or Table View, fld as a Field name (currently this will only be the OID field), and the delete_dq as a Boolean gives the user a choice to delete any previous definition queries set on the layer/table or to simply add a new definition query.

## input feature layer or table from the APRX
in_table = arcpy.GetParameter(0)

## the field to base the definition query on
fld = arcpy.GetParameterAsText(1)

## delete any previous def queries on the layer/table or not
delete_dq = arcpy.GetParameterAsText(2)

We’ll create a function that returns a unique list of values from the fld input using the Search Cursor and set comprehension. At the moment we are only catering for the OID field which is always unique but this function will give us the option to expand for other unique field types in the future or create more complex definition queries based on selections.

def getUniqueValues(table, field):
    """
    Get a list of unique values from a single field in a table

    Args:
        table (str):  table or feature class in a geodatabase
        field (str):  field to get unique values from

    Returns:
        a sorted set of unique entries
    """
    with arcpy.da.SearchCursor(table, [field]) as cursor:
        return sorted({row[0] for row in cursor if row[0] != None})

We have a few required objects to help with the workflow. We create an ArcGISProject object for the open APRX. We then check whether the input from our first parameter (in_table) is a Layer or a Table and use that ArcGISProject object to access the layer or table object via the active map. Last, we need the field type from the fld input parameter, it will be OID for now and we will limit this in the parameters validation of the tool in ArcGIS Pro.

## access the APRX
aprx = arcpy.mp.ArcGISProject("CURRENT")

## figure out if input is a layer or table and access the layer or table
## in the active map
if isinstance(in_table, arcpy._mp.Layer):
    in_table = aprx.activeMap.listLayers(in_table.longName)[0]

elif isinstance(in_table, arcpy._mp.Table):
    in_table = aprx.activeMap.listTables(in_table.longName)[0]

## get the field type from the fld parameter
fld_type = [field.type for field in arcpy.ListFields(in_table) if field.name == fld][0]

We then call our getUniqueValues function

## get unique values to base definition query on
values = getUniqueValues(in_table, fld)

If the user selects to delete any previous definition queries, we will remove them before adding the new query.

if delete_dq == "true":
    in_table.updateDefinitionQueries(None)

All there is left to do is create the new definition query. We’ll add a warning if a field type was selected that we do not handle just yet, this won’t come into play as we will limit input to an OID field type but we will look at upgrading this tool in the future.

## only handles OID fields for now
if fld_type in ("OID") :
    in_table.definitionQuery = "{0} IN ({1})".format(fld, ",".join(str(x) for x in values))

else:
    arcpy.AddWarning("Field type not supported at this time")

Save your script and open up ArcGIS Pro. Right-click on your toolbox/toolset of choice and select New > Script. The New Script window will appear. In the General tab set Name to defQueryFromSelectionLabel to Add Definition Query based on Selection, and the Description as per below or any description you feel is apt.

In the Parameters tab set as below. Set the Data Type for the Input Layer or Table to Feature Layer, Table View. Set the Filter for the Query Field to OID field only, and set the Dependency to the first parameter in_table. Set the Type to Optional for the Delete Previous Definition Queries and the Default to true.

In the Execution tab, click the folder icon in the top-right corner and add your saved Python script.

In the Validation tab we want set an error message if a selection has not been made on the layer/table as we do not want to ever create a definition query for the entire dataset.

    def updateMessages(self):
        # Modify the messages created by internal validation for each tool
        # parameter. This method is called after internal validation.
        if self.params[0].altered:
            desc = arcpy.Describe(self.params[0].value)
            if desc.FIDSet:
                self.params[0].clearMessage()
            else:
                self.params[0].setErrorMessage("There must be a selection on the layer/table")
        return

You can download the tool and other custom tools over on this page. This tool is in the Custom Tools on a Basic License with ArcPy section.

Here’s the script above in its entirety.

import arcpy

################################################################################
## Documentation Links:
##  https://pro.arcgis.com/en/pro-app/3.2/arcpy/functions/getparameterastext.htm
##  https://pro.arcgis.com/en/pro-app/3.2/arcpy/data-access/searchcursor-class.htm
##  https://pro.arcgis.com/en/pro-app/3.2/arcpy/mapping/arcgisproject-class.htm
##  https://pro.arcgis.com/en/pro-app/3.2/arcpy/mapping/map-class.htm
##  https://pro.arcgis.com/en/pro-app/3.2/arcpy/functions/listfields.htm
##  https://pro.arcgis.com/en/pro-app/3.2/arcpy/classes/field.htm
##  https://pro.arcgis.com/en/pro-app/3.2/arcpy/mapping/layer-class.htm
##  https://pro.arcgis.com/en/pro-app/3.2/arcpy/functions/addmessage.htm
##  https://pro.arcgis.com/en/pro-app/3.2/arcpy/functions/addwarning.htm
##
## ArcGIS Pro 3.2.0
##
################################################################################

################################################################################
## USER INPUTS #################################################################

## input feature layer or table from the APRX
in_table = arcpy.GetParameter(0)

## the field to base the definition query on
fld = arcpy.GetParameterAsText(1)

## delete any previous def queries on the layer/table or not
delete_dq = arcpy.GetParameterAsText(2)

################################################################################
## FUNCTIONS ###################################################################

def getUniqueValues(table, field):
    """
    Get a list of unique values from a single field in a table

    Args:
        table (str):  table or feature class in a geodatabase
        field (str):  field to get unique values from

    Returns:
        a sorted set of unique entries
    """
    with arcpy.da.SearchCursor(table, [field]) as cursor:
        return sorted({row[0] for row in cursor if row[0] != None})

################################################################################
## REQUIRED OBJECTS ############################################################

## access the APRX
aprx = arcpy.mp.ArcGISProject("CURRENT")

## figure out if input is a layer or table and access the layer or table
## in the active map
if isinstance(in_table, arcpy._mp.Layer):
    in_table = aprx.activeMap.listLayers(in_table.longName)[0]

elif isinstance(in_table, arcpy._mp.Table):
    in_table = aprx.activeMap.listTables(in_table.longName)[0]

## get the field type from the fld parameter
fld_type = [field.type for field in arcpy.ListFields(in_table) if field.name == fld][0]

################################################################################
## GET UNIQUE VALUES ###########################################################

arcpy.AddMessage("Getting unique values")

## get unique values to base definition query on
values = getUniqueValues(in_table, fld)

################################################################################
## DELETE DEF QUERIES ##########################################################

if delete_dq == "true":
    arcpy.AddMessage("Deleting previous definition queries")
    in_table.updateDefinitionQueries(None)

################################################################################
## UPDATE DEFINITION QUERY #####################################################

arcpy.AddMessage("Updating Definition Query")

## only handles OID fields for now
if fld_type in ("OID") :
    in_table.definitionQuery = "{0} IN ({1})".format(fld, ",".join(str(x) for x in values))

else:
    arcpy.AddWarning("Field type not supported at this time")

################################################################################

Leave a Comment

Your email address will not be published. Required fields are marked *