If you have spent any time configuring Odoo, you have probably come across domain expressions. Those bracket-filled filter conditions appear in action buttons, automated rules, record rules, and email templates throughout the platform. But fewer people realize that Odoo also provides a dedicated Domain field type in its ORM, designed specifically to store and display these expressions as structured data.
Understanding domain fields matters whether you are an Odoo developer building custom modules or a business user configuring automated workflows. This guide covers everything from what a domain field actually stores to practical examples, development tips, and the most common mistakes to avoid.
What is the Domain Field in Odoo
In Odoo, a domain is a list of filter conditions used to query records. It follows a specific syntax using tuples and logical operators, and it maps directly to SQL WHERE clauses when Odoo runs a database query.
A typical domain expression looks like this:
[('customer_rank', '>', 0), ('active', '=', True)]
This translates to: return all records where customer_rank is greater than 0 AND active is True.
The Domain field (fields.Domain) is a field type in the Odoo ORM that stores these expressions as structured data inside a record. Unlike a plain text field, it comes with built-in validation and a dedicated UI widget that lets users build filter conditions visually, without writing any code.
How it appears in the interface
In the Odoo interface, a Domain field renders using the domain editor widget by default. This widget presents a visual rule builder where users can pick a field, select an operator (equals, contains, greater than, and so on), and enter a value. It is the same interface you see when configuring access rules or filter criteria in automated actions.
In the database, domain values are stored as plain text, specifically as the string representation of a Python list. The field type handles serialization and validation transparently, so developers work with clean domain values in Python while the storage layer handles the rest.
This is part of the broader Odoo data model design: fields are not just storage containers. They carry semantic meaning, UI behavior, and validation logic together in a single definition.
How the Field Works
The Domain field integrates tightly with the Odoo ORM and record filtering system. Here is what happens under the hood when you save and evaluate a domain.
Storage and representation
When a domain is saved to the database, Odoo stores it as a serialized string. A domain restricting records to draft state for the current user is stored as a text value that Odoo evaluates at query time. The special variable uid is resolved to the current user ID at runtime, making dynamic filtering possible without hardcoding values.
The Odoo ORM takes that string, evaluates it safely, and converts it into a SQL WHERE clause. This evaluation happens through safe_eval, which supports a controlled subset of Python expressions along with Odoo-specific context variables.
The domain widget
In the Odoo interface, fields of type fields.Domain use the domain widget by default. This widget presents the filter builder you see across many parts of Odoo. Users can add conditions, combine them with AND or OR logic, and preview results without touching any code.
The widget is what makes domain fields genuinely accessible to business users. You do not need to understand domain syntax to configure a filter. The visual builder handles the translation for you.
Model context
A Domain field can be linked to a specific model, which tells Odoo which fields to offer in the domain builder. You set this via the model_field attribute in the field definition. Without a model context, the domain widget falls back to a plain text input, which is far less useful for end users.
This context binding is a core part of how the Odoo framework connects field definitions to UI behavior. The field knows about the model it filters, and the interface adapts accordingly.
Interaction with other records
Domain fields are often used alongside relational fields to restrict which records appear in a Many2one dropdown, to define which records an automated action targets, or to set the scope of a report or dashboard. The domain expression acts as a live filter applied at the ORM level, meaning it respects Odoo security rules and field access controls.
Business Use Cases
Domain fields appear across virtually every Odoo module. Here are five real-world examples that show how they drive practical business workflows.
1. Automated actions and email triggers
When you configure an automated action in Odoo (Settings > Technical > Automation), you define a domain to specify which records trigger the action. For example, an automated email for overdue invoices might target only posted invoices that are unpaid and past their due date. This domain is stored directly on the base.automation model in a Domain field called filter_domain. The action only fires for records that match.
2. Record rules and access control
Odoo security record rules use Domain fields to restrict which records a user group can see or edit. A sales team rule might limit visibility to records assigned to the current user team, evaluated at query time. This gives you row-level security without writing custom code. Every filter on every record rule in Odoo is a domain expression stored in a Domain field.
3. Inventory and operations filtering
In warehouse and inventory management, scheduled actions and reordering rules use domains to target specific product categories, locations, or stock levels. An automated restock action can be scoped to storable products with available quantity at or below a reorder point, preventing unnecessary processing across thousands of records.
4. CRM pipeline and lead qualification
In the CRM module, pipeline stage automation, activity rules, and lead assignment all rely on domain expressions to categorize and qualify leads. Custom lead assignment rules use domains to match leads to the right salesperson based on country, industry, or deal size. The domain field is what makes these rules configurable from the interface rather than requiring code changes for every new scenario.
5. Dynamic Many2one dropdowns
In custom forms, a domain applied to a Many2one field controls which records appear in the dropdown. Restricting a vendor field to show only active suppliers with a non-zero supplier rank creates more focused user experiences and reduces input errors. The domain can even be dynamic, referencing values from other fields on the same form, making the available options change based on user selections.
Creating or Customizing the Field
There are two main approaches to working with Domain fields in Odoo: using Odoo Studio for no-code customization, or writing Python and XML in a custom module.
Using Odoo Studio
Odoo Studio does not currently expose a standalone Domain field type in its visual field creator. For most business configuration needs, you do not need one. The existing domain editors built into automated actions, record rules, and server actions give you the visual domain builder without requiring a custom field.
If you want to add a domain filter to a Many2one field on a form, you can do that in Studio by editing the field properties and entering a domain expression directly. Studio validates the domain syntax and stores it on the view definition.
Technical customization in Python
In a custom Odoo module, adding a Domain field is a straightforward part of the standard odoo developer guide. Here is a basic example using the ORM:
from odoo import models, fields
class MyModel(models.Model):
_name = 'my.model'
model_name = fields.Char(default='res.partner')
filter_domain = fields.Domain(
string='Filter Domain',
model_field='model_name',
help='Domain expression to filter partner records'
)
The model_field attribute links the domain editor to the model name stored in model_name. This tells the domain widget which fields to offer in the visual builder. Keeping the model name in a separate field also allows dynamic model selection if your use case requires it.
Adding the widget to a form view
To display the domain builder in a form view, reference both the model field and the domain field in the view XML:
<field name="model_name" invisible="1"/>
<field name="filter_domain" widget="domain"
options="{'model': 'model_name'}"/>
Without the widget="domain" declaration and the model option, the field renders as plain text. Always include both when building forms that expose domain configuration to users.
Writing domain values via the XML-RPC API
If you are setting domain field values programmatically via the API, always pass the value as a string:
models.execute_kw(db, uid, api_key, 'my.model', 'write',
[[record_id], {
'filter_domain': "[('active', '=', True)]"
}]
)
Passing a Python list object instead of a string is a common mistake that causes errors or silent failures depending on the Odoo version. Always serialize your domain to a string before writing it through the API.
Best Practices
These practical habits will save you time and prevent issues when working with domain fields in Odoo.
Validate domain syntax before deploying
An invalid domain expression raises errors the moment Odoo tries to evaluate it. Test your domains in the Odoo search bar or in developer mode before saving them to automated actions or record rules. A quick search_count call via the API is also a reliable way to confirm a domain returns the expected number of records.
Use dynamic variables where possible
Avoid hardcoding user IDs, company IDs, or dates in domain expressions. Use dynamic variables like uid, context_today(), and current_company_id instead. This keeps your domains portable and avoids silent breakage when records change between environments.
Always bind the model context
When adding a Domain field to a custom model, always set the model_field attribute and include it in the view. Without it, users see a plain text input instead of the visual domain builder, which reduces usability and increases the chance of invalid values being saved.
Keep domains readable
Complex nested domains using | (OR) and & (AND) operators can become hard to read and maintain. Add comments in your Python code explaining the intent behind each domain. If a domain grows very complex, consider whether a server action or computed field would be clearer and easier to test.
Use safe_eval for programmatic evaluation
When evaluating domain strings in Python code, for example inside a server action or automated action, use Odoo built-in safe_eval rather than Python native eval. It is safer, handles Odoo-specific context variables correctly, and is consistent with how Odoo evaluates domains internally.
Test with realistic data
Always verify your domain matches the expected records before going live. This is especially important for automated actions and record rules, where an incorrect filter can process the wrong records or block user access without any visible warning.
Common Pitfalls
Here are the mistakes that come up most often when working with domain fields in Odoo, and how to avoid them.
Confusing the field type with domain syntax
The word "domain" means two different things in Odoo. It refers to the filter syntax (a list of tuples), and it also refers to fields.Domain, a specific ORM field type that stores those expressions as data. Newcomers to Odoo customization often mix the two up. A Domain field is a storage container. The domain expression is the filter logic inside it.
Passing a list instead of a string via the API
When writing to a Domain field via XML-RPC, you must pass a string, not a Python list object. Passing the domain as a raw list will cause a type error or silent failure depending on the Odoo version. Always serialize your domain to a string before writing it through the API.
Missing model context on the widget
If you add a Domain field to a form view without specifying the model context in the widget options, users will see a plain text input rather than the visual domain builder. The domain builder only appears when the widget knows which model to reference. Always include the model_field link in your view definition.
Hardcoding record IDs in domains
Domains that reference specific record IDs directly break silently when those records are deleted or when you copy the configuration to a new database. Use dynamic references like uid or relational lookups wherever possible to keep your domains portable.
Overly broad or restrictive record rule domains
A record rule domain that is too permissive can expose records to users who should not see them. One that is too restrictive can silently hide records without any explanation to the user. Always test record rule domains from the perspective of the target user group, not the admin account that bypasses all rules.
Forgetting archived records
By default, Odoo excludes archived records (where active = False) from search results. If your domain does not account for this, you may get unexpected gaps in your data. Add ('active', 'in', [True, False]) when you need to include archived records in the filter results.
Conclusion
Domain fields are one of those building blocks that quietly power a large part of how Odoo works. From access control and automated actions to dynamic dropdowns and dashboard filters, domain expressions are the backbone of record filtering in Odoo, and the fields.Domain type gives developers a clean, validated way to store and present that logic directly in the Odoo data model.
For business users, the domain widget makes filter configuration accessible without any code. For developers, the Domain field type brings clarity to model definitions that used to rely on generic Char fields with a widget override. Whether you are working in Odoo Studio, writing a custom Python module, or configuring automated workflows from the interface, understanding how domain fields work opens up a wide range of possibilities.
The concepts covered in this guide apply across Odoo versions and modules. Time spent understanding domain fields is time well invested, because they are genuinely everywhere in Odoo.
Need help with your Odoo implementation?
Dasolo helps companies implement, customize, and optimize Odoo for their specific business needs. Whether you are setting up automated workflows, building custom modules, or trying to get more out of your existing Odoo setup, our team has the technical depth to help you move forward with confidence.
If you have questions about Domain fields or any other aspect of your Odoo implementation, reach out to us. We are happy to take a look at your setup and point you in the right direction.