If you’re moving from ArcGIS Pro to QGIS, Attribute Rules are one of the first things you’ll miss. ArcGIS Pro’s Attribute Rules let you attach Arcade scripts to a geodatabase layer that fire automatically on edit — calculating fields, enforcing constraints, validating records. QGIS has equivalents for all three categories, but they work differently, and none of them are geodatabase-level rules that execute independently of the client application.

This matters: QGIS’s field rules live in the project file, not the data source. If someone opens your GeoPackage or Shapefile in a different QGIS project — or in a different GIS tool — those rules don’t travel with the data. That’s a significant architectural difference from ArcGIS Pro, where rules are stored in the geodatabase and enforced by the database engine.

With that constraint understood, here’s how to replicate each category.

ArcGIS Attribute Rule Types and Their QGIS Counterparts

ArcGIS Rule TypeWhat It DoesQGIS Equivalent
Calculation (immediate)Populates a field on editDefault value expression + virtual field
Calculation (batch)Recalculates existing recordsField Calculator (manual) / Processing algorithm
ConstraintBlocks save if value is invalidField constraint + expression
ValidationFlags records that violate a ruleTopology Checker / virtual field with error flag
Contingent valuesLimits values based on another fieldWidget config with filter expression

Calculation Rules — Default Value Expressions

In ArcGIS Pro, an immediate calculation rule fires when you create or edit a feature, automatically populating a field using Arcade. The closest QGIS equivalent is the Default Value Expression on a field.

Set it via Layer Properties > Fields > double-click a field > Default Values tab:

# ArcGIS Arcade (calculation rule):
return Upper($feature.city_name)

# QGIS equivalent (default value expression):
upper("city_name")

QGIS expressions use the same expression engine as labels, symbology, and filters — so the syntax is consistent across the application.

Default value expressions fire when:

  • A new feature is created in the attribute form
  • The “Apply default values” option is active

They do NOT fire when:

  • Editing existing features
  • Data is imported via Processing or ogr2ogr
  • Another application writes to the same GeoPackage

For attribute calculation on existing records, use the Field Calculator (Open Field Calculator button in the attribute table). This is always a manual step — there’s no equivalent to ArcGIS Pro’s background recalculation.

Constraint Rules — Field Constraints

ArcGIS Pro constraint rules prevent a save if a field value violates a condition. QGIS implements this through field constraints on each field.

Access via Layer Properties > Fields > select a field > Constraints:

  • Not null — value must exist
  • Unique — value must be unique across all features in the layer
  • Expression constraint — arbitrary QGIS expression that must evaluate to true

The expression constraint is the flexible one. Examples:

# Value must be one of three statuses
"status" IN ('active', 'pending', 'closed')

# Approval date must not precede creation date
"approval_date" >= "creation_date" OR "approval_date" IS NULL

# Length field must be positive when geometry exists
$geometry IS NULL OR "length_m" > 0

Constraints are evaluated when the feature is saved in the attribute form. If the constraint fails, QGIS shows an error message and doesn’t commit the edit.

Important limitation: constraints are enforced at the form level. Bulk operations via the Field Calculator, Python, or Processing bypass form validation entirely. This is fundamentally different from ArcGIS Pro geodatabase constraints, which enforce at the database write level regardless of client.

Calculated Fields That Update on Edit

ArcGIS calculation rules that fire on update (not just on create) have no direct QGIS form equivalent. The workaround is virtual fields.

A virtual field is a non-stored computed column — QGIS evaluates it on the fly from other field values. It doesn’t update the underlying data; it’s calculated fresh each time it’s displayed or used.

Add via Layer Properties > Fields > Add new field > check “Create virtual field”:

# Concatenate first and last name
concat("first_name", ' ', "last_name")

# Calculate area in hectares from geometry
$area / 10000

# Flag records where population density is suspiciously high
CASE WHEN "population" / ($area / 1000000) > 50000 THEN 'flag' ELSE 'ok' END

Virtual fields work for read-only calculated values. If you need the calculated value stored in the database (for export, for filter indexes, for other tools to read), you need to run the Field Calculator to write it explicitly.

Validation Rules — Topology Checker Plugin

ArcGIS Pro validation rules flag existing records that violate a spatial or attribute rule, without blocking edits. The spatial equivalent in QGIS is the Topology Checker plugin (Plugins > Manage Plugins > Topology Checker).

The Topology Checker can detect:

  • Must not have duplicates
  • Must not have invalid geometries
  • Must not have gaps (polygon layers)
  • Must not overlap
  • Must be covered by (relationship between two layers)
  • Points must be covered by lines
  • … and about 15 other topology rules

Configure a rule set, click “Validate All” or “Validate Extent,” and the plugin populates an error list. Errors are selectable — clicking an error zooms to the offending feature.

This is a manual validation step, not an automated background check. You run it when you want it; it doesn’t fire on save. For ArcGIS teams used to always-on validation with error flagging in the attribute table, this requires a workflow change.

For attribute-level validation (not spatial), write a virtual field that flags records:

# Flag features where field values are inconsistent
CASE
  WHEN "material_type" = 'concrete' AND "texture" = 'wood' THEN 'inconsistent'
  WHEN "install_year" > year(now()) THEN 'future_date'
  ELSE 'ok'
END

Then filter the attribute table to "validation_status" != 'ok' to see your errors.

Contingent Values — Filtered Value Relations

ArcGIS Pro contingent values restrict a field’s allowed values based on the value of another field (e.g., “Subtype” determines which values are valid for “Condition”). QGIS replicates this with Value Relation widgets combined with filter expressions.

In Layer Properties > Attributes Form > select the dependent field:

  1. Set Widget Type to “Value Relation”
  2. Point to a lookup table (can be a GeoPackage table, CSV, or layer)
  3. Set Filter Expression: "material_type_key" = current_value('material_type')

The dropdown only shows values from the lookup table where the filter matches the current value of the parent field. This requires a properly structured lookup table — the same data modeling discipline ArcGIS Pro’s contingent values require.

Python Form Init Scripts

For rules that require more logic than QGIS expressions support, Python form init scripts give you full control over the attribute form’s behavior.

Configure via Layer Properties > Attributes Form > set “Python Init Function” and “Module path”:

# forms/my_layer_form.py

def formOpen(dialog, layer, feature):
    """Called when the attribute form opens."""
    status_widget = dialog.findChild(QComboBox, 'status')
    date_widget = dialog.findChild(QDateEdit, 'created_date')

    # Disable date field for existing features
    if feature.id() > 0:
        date_widget.setEnabled(False)

    # Update allowed values based on feature state
    current_status = feature['status']
    if current_status == 'closed':
        status_widget.setEnabled(False)

This approach can replicate nearly any form-level business rule from ArcGIS Pro. The limit is the same as all QGIS client-side rules: it only fires in QGIS’s form dialog, not in bulk operations or external writes.

Replicating the Full ArcGIS Rule Set in QGIS

Here’s a practical mapping for a common data management scenario — a parcels layer with ownership, zoning, area, and validation fields:

ArcGIS Pro Attribute Rules needed:

  • Constraint: parcel_id must be unique and not null
  • Calculation (immediate): full_address = concat(situs_number, ' ', street_name)
  • Calculation (on update): last_modified = NOW()
  • Validation: zoning_code must match a valid code from a reference table
  • Contingent: allowed use_type values depend on zoning_code

QGIS implementation:

parcel_id:
  Constraint: Not null + Unique

full_address:
  Widget: Text Edit (read-only)
  Virtual field: concat("situs_number", ' ', "street_name")

last_modified:
  Default value: now()
  Note: Only sets on feature creation in QGIS form.
        For update-on-edit behavior, use a Python form script.

zoning_code:
  Widget: Value Relation → zoning_reference table
  Constraint expression: 
    "zoning_code" IN (array_to_string(aggregate('zoning_reference', 'array_agg', "code")))

use_type:
  Widget: Value Relation → use_type_reference
  Filter expression: "zoning_category" = current_value('zoning_code')

The Fundamental Constraint

All of the above lives in your .qgs or .qgz project file. None of it travels with the data layer. If you share a GeoPackage with a colleague, they get the data but not the form configuration, default expressions, or constraint definitions unless you also share the project file or they configure their own project identically.

This is the strongest reason not to use QGIS form rules as a substitute for data integrity enforcement in a multi-user environment. For that use case, the appropriate layer is database-level constraints (PostGIS CHECK constraints, UNIQUE constraints, trigger functions) that enforce at the database engine regardless of client.

For single-user or single-project work — which covers most QGIS deployments — the combination of field constraints, default expressions, virtual fields, and Topology Checker covers the practical use cases that ArcGIS Attribute Rules serve.

Format Considerations for Migrated Data

When migrating data from an ArcGIS geodatabase to QGIS-compatible formats, field constraints and domain values aren’t preserved in the conversion. The data values transfer; the rules do not.

If your ArcGIS geodatabase has coded value domains or range domains, these are exported as raw attribute values without any constraint metadata in GeoJSON, Shapefile, or GeoPackage formats. You’ll need to recreate the constraints in QGIS manually.

GeoPackage is the best target format for migrated data — it supports multiple layers, long field names, and a proper attribute table schema. Shapefile’s 10-character field name limit and its data corruption issues with field truncation make it a poor choice for preserving complex attribute schemas from ArcGIS.

For format conversion between ArcGIS-originated formats and GeoPackage, GeoConvert handles CRS preservation and geometry validation automatically — including the geometry repair that GDAL’s ogr2ogr applies silently and that ArcGIS exports sometimes need.