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 Type | What It Does | QGIS Equivalent |
|---|---|---|
| Calculation (immediate) | Populates a field on edit | Default value expression + virtual field |
| Calculation (batch) | Recalculates existing records | Field Calculator (manual) / Processing algorithm |
| Constraint | Blocks save if value is invalid | Field constraint + expression |
| Validation | Flags records that violate a rule | Topology Checker / virtual field with error flag |
| Contingent values | Limits values based on another field | Widget 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:
- Set Widget Type to “Value Relation”
- Point to a lookup table (can be a GeoPackage table, CSV, or layer)
- 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_idmust be unique and not null - Calculation (immediate):
full_address = concat(situs_number, ' ', street_name) - Calculation (on update):
last_modified = NOW() - Validation:
zoning_codemust match a valid code from a reference table - Contingent: allowed
use_typevalues depend onzoning_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.