Skip to main content
iVentureTeam

domain

The point-and-click filter builder behind automation rules, mailing lists and reordering rules. The domain widget turns a char field into Odoo's condition editor, live record count included.

Siddharth JambukiyaSiddharth JambukiyaOdoo Techno-Functional Consultant
August 12, 2026Updated August 12, 20267 min read
Odoo 19 form view showing the domain widget's visual filter builder with condition rows, operators, and the live matching records counter button.
Technical namedomain
Field typeschar, text
Viewsform
Moduleweb, present in every Odoo database
Used in core15 occurrences across 9 modules, including base_automation, crm, loyalty, mass_mailing, stock, event_crm
VersionsOdoo 20.0, Odoo 19.0, Odoo 18.0
No-code setupNo. There is no Studio path for domain fields; the widget is applied in XML.
Alternativeschar, text

What the Domain field does

A domain in Odoo is a filter expression, the bracketed condition list like [('state', '=', 'sale')] that powers every search, automation trigger and targeted mailing. Stored on a record, it is just a string in a char field. This widget replaces that string with the full visual builder: condition rows with field pickers and operators, and or grouping, a live count of matching records, and a button that opens the actual matching records in a dialog so users can eyeball what their filter caught.

Core leans on it wherever a business user defines "which records": automation rule triggers in base_automation, lead assignment rules in crm, mailing list filters in mass_mailing, loyalty rule conditions, stock reordering scopes. One widget is the difference between those features being admin friendly and being developer only.

What this means for your team

The record count is the quiet hero here. A filter that looks right and matches 0 records, or 4 million, is the classic automation accident: the mass mailing that goes to nobody, the pricing rule that applies to everything. Surfacing "1,254 record(s)" next to the condition, with a click through to the list, catches those mistakes at authoring time instead of at 2 a.m.

For managers rolling out automations: treat the count as a required review step. The person writing the rule states the number they expect, the widget shows the number that is real, and the two get reconciled before activation. That one habit prevents most automation incidents we get called in for, and it costs nothing because the widget already computes the number.

Supported options in Odoo 19

Verified against domain_field.js in the Odoo 19.0 web module. Five declared options, all functional, plus one behavior hiding inside model that the documentation does not mention.

OptionTypeWhat it does
modelstring or field nameTarget model for the builder. Checked first against the record's field names: if it matches one, the model is read dynamically from that field's value; otherwise it is used as a literal model name. Without a resolvable model the builder cannot offer fields or counts.
allow_expressionsbooleanPermits non literal domains referencing things like user.id or date math, with a warning that evaluation might fail. Off, such domains mark the field invalid. Odoo 18's automatic exception for base.automation and ir.filters models no longer exists in 19.(default: false)
in_dialogbooleanReplaces inline editing with an edit button that opens the domain selector in a dialog, with debug mode exposed inside it.(default: false)
foldablebooleanStarts the field collapsed into readable facet chips describing each condition, with the full builder available on unfold. Facets load per condition through the tree processor.(default: false)
count_limitnumberCap for the record counter. The widget probes with limit + 1 and shows the capped value flagged as a lower bound when reached, avoiding full counts on large tables.(default: 10000)(since Odoo 19.0)

model is dual natured. The widget first checks whether the value names a field on the current record. If it does, the target model is read from that field's value at runtime, re-resolving as it changes; only otherwise is it treated as a literal model name. That is how one automation rule form serves every model in the database. The flip side: a field name typo silently becomes a wrong literal model, which is worth a test after every rename.

Working examples

Fixed model: filter contacts

<field name="partner_domain"
       widget="domain"
       options="{'model': 'res.partner'}"/>

Dynamic model read from another field

<field name="model_name" invisible="1"/>
<field name="filter_domain"
       widget="domain"
       options="{'model': 'model_name'}"/>

Because model_name is a field on the record, the builder retargets whenever its value changes. This is the base_automation pattern.

Expressions, dialog editing and a tighter count

<field name="assign_domain"
       widget="domain"
       options="{'model': 'crm.lead', 'allow_expressions': True, 'in_dialog': True, 'count_limit': 1000}"/>

With in_dialog, the inline builder is replaced by an edit button opening the selector in a dialog, which suits crowded forms.

Counts, expressions and debug mode

How the count works. The widget calls search_count silently with a limit of count_limit + 1. If the result reaches the probe limit, it displays the capped count and flags it as limited, which is why huge datasets show 10000+ instead of hanging the form on a full count. The count button opens a single select, no create dialog of the matching records, and the counter refreshes when that dialog closes, because records can be edited from inside it.

What counts as an expression. Literals like [('active', '=', True)] evaluate client side. The moment the domain references non literals, user.id, context_today(), relative date math, the widget flags it. Without allow_expressions the field is marked invalid with the notification that the domain should not involve non literals; with it, you get a softer warning that evaluation might fail, because the client cannot know what the server will inject. The count in expression mode is computed against the evaluated form when possible.

Developer mode changes the contract. With debug on, the raw domain string becomes editable next to the builder. Edits there bypass the visual tree and are checked server side through /web/domain/validate when the record saves, setting the field invalid on failure rather than letting a broken domain through. The foldable option adds one more layer: the domain collapses into readable facet chips, loaded through the tree processor, with the builder a click away.

Version compatibility

VersionStatusNotes
Odoo 20.0In developmentNot released. No option changes visible on the development branch; validation RPC becomes cached. Details below.
Odoo 19.0VerifiedVerified against the shipped source. Adds text field support and count_limit, removes the 18 expression exception.
Odoo 18.0Verifiedchar fields only, no count_limit, and expressions were auto-allowed on base.automation and ir.filters models.

Upgrade note for 18 to 19. Two real changes. First, the widget now also accepts text fields, where 18 declared char only. Second and more important: Odoo 18 automatically permitted expressions when the record's model was base.automation or ir.filters, and 19 dropped that hardcoded exception, so custom views on those models relying on the implicit pass need an explicit allow_expressions: True after migrating. count_limit is also new in 19; earlier versions always probed with the fixed default.

What is changing in Odoo 20

Odoo 20 is expected at Odoo Experience in Brussels, 24 to 26 September 2026. What follows is read from the public development branch, which is unstable until feature freeze; we re-verify this page against the shipped release.

No option changes are visible. All five options survive with identical names and defaults, a rarity in this batch of pages. The visible movement is operational: the /web/domain/validate RPC becomes cached, so repeated validation of the same domain string stops hitting the server, and the field claims full width through an added w-100 class. Props declarations migrate to the new schema system without behavior change.

In short, domains built on 19 should carry to 20 untouched, and we will confirm that against the release build.

Common problems and fixes

SymptomCause and fix
The builder shows no fields and no countThe model option is missing, or names a field that is empty on this record. Set options model to a literal model or ensure the referenced model field holds a value.
"The domain should not involve non-literals" after upgrading to 19Odoo 18 silently allowed expressions on automation and filter models; 19 requires opting in. Add allow_expressions True to the field's options in the custom view.
Counter reads 10000+ instead of an exact numberThe count probe reached count_limit, the intended guard for large tables. Raise count_limit if the exact number matters and the table can take the count.
Count seems wrong after editing records from the count dialogIt is not: the counter reloads when the dialog closes precisely because records can change inside it. No action needed; reopen the dialog to re-verify.
A hand typed domain saves on one form and fails on anotherRaw editing exists only in developer mode and is validated server side on save. Fix the reported syntax, or rebuild the condition visually.
Builder targets the wrong model entirelyThe model option value was meant as a field name but no longer matches one, so it is treated as a literal model. Re-point the option after any field rename.

Domain field vs the alternatives

WidgetBest forKey difference
domainBusiness editable record filters with a live countFull visual builder writing standard domain syntax
charDeveloper only domains nobody edits in the UIRaw bracket syntax with no builder, count or validation
textLong stored expressions reviewed rather than editedMultiline raw text, no domain awareness

There is no substitute widget in core: either the string gets the builder, or users hand write bracket syntax. The honest alternative decision is not which widget but whether the domain belongs on a record at all, versus living in the view or the automation directly.

Frequently asked questions

What is the domain widget in Odoo?+
It renders a char or text field holding a domain string as Odoo's visual filter builder, with condition rows, a live count of matching records and a dialog listing them. It is the editor behind automation rule triggers, mailing filters and similar "which records" fields.
How do I set which model the domain filters?+
Through the model option. Give it a literal model name like res.partner, or the name of a field on the same record, in which case the widget reads the target model from that field's value at runtime and re-targets as it changes.
Why does my domain field say non-literals are not allowed?+
The stored domain references expressions such as user.id or date arithmetic, and allow_expressions is off. Enable it in the field's options to accept them with a soft warning. Note that Odoo 18 allowed this automatically on automation and filter models and Odoo 19 does not.
Can users see which records their filter matches?+
Yes. The widget shows a running search_count, capped at count_limit which defaults to 10000, and clicking the count opens a dialog listing the matching records read only, refreshing the count when closed.
Can I edit the raw domain string directly?+
Only with developer mode active, which exposes the raw string next to the builder. Raw edits are validated server side through /web/domain/validate when saving, and a failing domain marks the field invalid instead of saving quietly.
Is there a no-code way to add a domain field?+
No. Odoo Studio has no Domain field type, so both the char field and the widget assignment happen in XML. Once in place, the builder itself is entirely no-code for end users, which is the point of the widget.
Does the domain widget work on text fields?+
Since Odoo 19, yes: supported types are char and text. Odoo 18 declared char only, which matters when backporting a view.
What changes for the domain widget in Odoo 20?+
Nothing visible at the option level on the development branch. Domain validation responses become cached and the field takes full width by default. We re-check after the September 2026 release.

Automation rules misfiring, or not firing at all?

Behind most broken automations sits a domain that matches the wrong records. We audit and rebuild Odoo automation rules, lead routing and targeted mailing filters, with the counts verified before anything goes live, and train your admins to keep them healthy.

Book a free consultation

How this page was produced

Verified by reading domain_field.js in the Odoo 19.0 web module in full, including getResModel, the count probing logic, the expression gate and the debug update path, then diffing against 18.0, which surfaced the removed automation exception and the new count_limit and text support, and against the public development branch for the Odoo 20 section. The dynamic model behavior was confirmed in the base_automation views that ship with core. Corrections reach us through the contact page.