Enable Dark Mode!
how-to-use-domains-in-odoo-19-from-basic-filters-to-smart-rules.jpg
By: Sruthi M

How to Use Domains in Odoo 19: From Basic Filters to Smart Rules

Technical Odoo 19 Odoo Enterprises Odoo Community

Domains in Odoo are expressions that filter records in database queries, search views, or field definitions. They define conditions similar to SQL WHERE clauses but written in Odoo’s domain syntax. When working with Odoo 19, one of the most powerful concepts you’ll encounter is domains. They control what users see, how records are filtered, and what data becomes available in forms, lists, and reports. Whether you're customizing views, building modules, or defining security rules, understanding domains is essential.

A domain is a filtering expression used to show only specific records based on conditions. It follows a simple structure: [('field_name', 'operator', value)]. Domains can be used in: Views (XML), Python (search, search_count, read_group), Actions, Record rules, and Context-driven logic.

Basic Domain Filters

In Odoo 19, basic domain filters are the simplest and most commonly used way to restrict records. They are used to decide which records should be shown, selectable, or processed in views, actions, and searches. Think of a domain as Odoo’s version of a SQL WHERE clause, written in a readable Python-like syntax.

A basic domain is written as a list of tuples. Each tuple has three elements:

('field_name', 'operator', value)

  • field_name > the field you want to filter on
  • operator > how the comparison is done
  • value > the value to compare with

Example:

[('state', '=', 'draft')]

Shows only records where the state is in draft.

Common Operators in Basic Domains

=Equal('state', '=', 'sale')
!=Not equal('state', '!=', 'cancel')
>Greater than('amount_total', '>', 5000)
<Less than('qty', '<', 10)
>=Greater or equal('date_order', '>=', '2025-01-01')
<=Less or equal('date_order', '<=', '2025-01-31')
inIn list('state', 'in', ['draft', 'sent'])
not inNot in list('state', 'not in', ['cancel'])
ilikeCase-insensitive contains('name', 'ilike', 'customer')

By default, Odoo combines multiple conditions using AND.

Example:

[('state', '=', 'sale'), ('amount_total', '>', 1000)]

Show records where state is ‘sale’ AND amount_total is greater than 1000. No special operator is needed for AND in basic domains.

Complex Logical Operators

When basic domain filters are not enough, complex logical operators allow you to build powerful conditions using OR, AND, and NOT logic. These operators help model real business rules directly in Odoo without writing SQL.

In Odoo 19, logical operators work the same across XML, Python, actions, and record rules.

Logical Operators

Logical operators are written before the conditions they apply to (prefix notation).

  1. OR Operator (|): Use | when any one of the conditions can be true.
  2. Example: ['|', ('state', '=', 'draft'), ('state', '=', 'sent') ]

    Here Show orders where the state is draft OR sent.

  3. AND Operator (&): Although AND is implicit in basic domains, you can explicitly use & when mixing with OR or NOT.
  4. Example: ['&', ('state', '=', 'sale'), ('partner_id.country_id.code', '=', 'US') ]

    Here orders must be confirmed AND belong to US customers.

  5. NOT Operator (!): Use ! to negate a condition.
  6. Example: ['!', ('state', '=', 'cancel') ]

    Here exclude Cancelled Records.

  7. Combining AND + OR (Nested Logic): This is where complex operators become powerful.
  8. Example:

    ['|',
        ('amount_total', '>', 10000),
        '&',
            ('state', '=', 'sale'),
            ('partner_id.is_company', '=', True)
    ]

    It shows records where Amount > 10,000, OR State is sale AND customer is a company.

Context-Based Domains

Context-based domains allow domains to change dynamically based on runtime information such as: Current user, Current company, Default values and Values passed from actions or buttons. They make domains smart and flexible, without hardcoding values.

The context is a dictionary (key: value) that carries temporary information across: Views, Actions, Wizards and Records. You can access it using: context.get('key') in XML and self.env.context.get('key') in Python.

Example:

<field name="partner_id"
       domain="[('company_id', '=', context.get('company_id'))]"/>

It shows only partners belonging to the active company.

Using Default Values from Context :

This is common in: Wizards, One2many popup forms and Smart buttons.

Example: Filter Based on Default Partner

<field name="contact_id"
       domain="[('parent_id', '=', context.get('default_partner_id'))]"/>

Context-Based Domain in Python:

This is useful when: Calling methods from actions, Handling logic from buttons  and Working inside wizards.

Example:

company_id = self.env.context.get('company_id')
orders = self.env['sale.order'].search([
    ('company_id', '=', company_id)
])

Filtering by Current User:

XML

<field name="task_id"
       domain="[('user_id', '=', uid)]"/>

Python

self.env['project.task'].search([
    ('user_id', '=', self.env.user.id)
])

Passing Context from an Action:

Action Definition

<record id="action_my_tasks" model="ir.actions.act_window">
    <field name="name">My Tasks</field>
    <field name="res_model">project.task</field>
    <field name="view_mode">tree,form</field>
    <field name="context">{'default_user_id': uid}</field>
</record>

Using It in Domain

<field name="user_id"
       domain="[('id', '=', context.get('default_user_id'))]"/>

Context with Multi-Company Domains:

<field name="product_id"
       domain="['|',
           ('company_id', '=', False),
           ('company_id', '=', context.get('company_id'))
       ]"/>

Meaning:  Allows shared products + company-specific products.

Dynamic Domains in Views

Dynamic domains in views allow Odoo to change available records in real time based on user input. Unlike static or context-based domains, these domains react when a field value changes—making forms smarter and more user-friendly.

In Odoo 19, dynamic domains are commonly implemented using: XML field references or @api.onchange methods.

A dynamic domain updates automatically when: A user selects or changes a field, Another field depends on that value. They are mainly used in: Form views, Wizards and One2many popup forms.

For example: Selecting a country filters available states.

XML-Based Dynamic Domains

Odoo can dynamically evaluate domains in XML using field names directly.

Example: Filter States by Selected Country

<field name="country_id"/>
<field name="state_id"
       domain="[('country_id', '=', country_id)]"/>

How It Works: When country_id changes, Odoo automatically recalculates the domain for state_id. And no Python code required.

Dynamic Domains Using @api.onchange

Use @api.onchange when: Logic is complex or Multiple fields affect the domain or Conditions cannot be expressed in XML.

Example: Filter Products by Category and Company

@api.onchange('category_id', 'company_id')
def _onchange_category_company(self):
    domain = []
    if self.category_id:
        domain.append(('categ_id', '=', self.category_id.id))
    if self.company_id:
        domain += ['|',
                   ('company_id', '=', False),
                   ('company_id', '=', self.company_id.id)]
    return {
        'domain': {
            'product_id': domain
        }
    }

Dynamic Domains in Wizards

Dynamic domains are heavily used in wizard forms.

Example: Filter Employees by Department

@api.onchange('department_id')
def _onchange_department_id(self):
    return {
        'domain': {
            'employee_id': [('department_id', '=', self.department_id.id)]
        }
    }

Security Domains

Security domains, implemented through record rules, are used to control which records a user can access in Odoo. Unlike view-level domains (which only affect the UI), security domains are enforced at all levels: Form views, List views, Search queries, Reports, and API calls. In Odoo 19, record rules are a critical part of building secure and reliable ERP systems.

Record Rules

A record rule is a domain applied automatically to a model that restricts access based on conditions. It defines: Which records can this user read, write, create, or delete?

Basic Record Rule Example:

<record id="rule_user_own_records" model="ir.rule">
<field name="name">Own Records Only</field>
<field name="model_id" ref="sale.model_sale_order"/>
<field name="domain_force">[('user_id', '=', user.id)]</field>
</record>

Meaning - A user can only access records where: user_id = current user

Combining Conditions:

['&',
    ('user_id', '=', user.id),
    ('state', '!=', 'cancel')
]

Here Own Records + Not Cancelled

Using Current User in Domains

You can use special variables:

  • user.id > current user ID
  • user.company_id.id > current company
  • uid > also current user ID

How Record Rules Work Internally

Record rules automatically applied to all ORM operations. It Combined with other rules using AND logic. And cannot be bypassed by normal code.

If multiple rules exist: They are combined using AND Access is granted only if all rules pass. This can accidentally restrict too much data if not designed carefully.

Multi-Company Domains

In Odoo 19, multi-company support allows a single database to manage multiple companies. Multi-company domains ensure that users only see relevant data based on their company access, while still allowing shared records where needed. They are essential for maintaining data isolation, consistency, and security in real-world ERP systems.

In a multi-company setup: Each company has its own data (sales, invoices, employees, etc.) and some data may be shared (products, contacts). Domains help answer: Which records should this user see in this company?

Example - Restrict to Current Company :

<field name="domain">[('company_id', '=', company_id)]</field>

Shows only records belonging to the active company.

['|',  ('company_id', '=', False),  ('company_id', '=', self.env.company.id)]

This is the most common pattern in Odoo.

Multi-Company Domain in Views

<field name="product_id"
       domain="['|',
           ('company_id', '=', False),
           ('company_id', '=', company_id)
       ]"/>

Filters product selection based on company context.

Multi-Company in Record Rules

<record id="rule_multi_company" model="ir.rule">
    <field name="name">Multi-Company Access</field>
    <field name="model_id" ref="base.model_res_partner"/>
    <field name="domain_force">
        ['|',
            ('company_id', '=', False),
            ('company_id', '=', user.company_id.id)
        ]
    </field>
</record>

Ensures users cannot access other companies' data.

Odoo supports users working in multiple companies at once.

[('company_id', 'in', self.env.context.get('allowed_company_ids'))]

Show records from all companies the user has selected in the UI. Very important in: Accounting, Inventory and Consolidated reporting.

To read more about Overview of Basic Domain Filters in Odoo19, refer to our blog Overview of Basic Domain Filters in Odoo19.


If you need any assistance in odoo, we are online, please chat with us.



0
Comments



Leave a comment



whatsapp_icon
location

Calicut

Cybrosys Technologies Pvt. Ltd.
Neospace, Kinfra Techno Park
Kakkancherry, Calicut
Kerala, India - 673635

location

Kochi

Cybrosys Technologies Pvt. Ltd.
1st Floor, Thapasya Building,
Infopark, Kakkanad,
Kochi, India - 682030.

location

Bangalore

Cybrosys Techno Solutions
The Estate, 8th Floor,
Dickenson Road,
Bangalore, India - 560042

Send Us A Message