Domain in Odoo

Domain is used in Odoo to select records from a Model (database table) – in many different places:

One way to see how it works is to create a new User-Defined Filter.

  • e.g. We want to see orders from customers in California.

Create a user-defined filter

The simplest way to do this is to create a filter from the “front-end”, as explained here.

Creating from the ‘back-end’

Enable “developer mode“, navigate to Technical / User-defined Filters and click on “New”:

Select a model (in this case Sale Order) and click on “Add Filter”.

Now we can select a field on sale.order. If it’s a one2many field we can select fields on the Related model (database table).

For this example, we’ll select “State” from the Invoice address

We can enter the name of the state:


You can copy and paste this domain and use it elsewhere.

A slightly more complex example: all products that can be purchased or sold. This uses the logical OR symbol “|” :


If there were three conditions you need to have OR twice (if there are four you need three ORs, and so on).

Other logical operators are:

  • &‘ = AND (this is the default so it is optional)
  • !‘ = NOT

There are many other operators:

  • =, !=, >, >=, <, <=
  • like, not like, ilike, not ilike     
  • in, not in    

You can experiment with them in User-Defined Filters to get a better understanding of the syntax.


Select Purchase Orders but not RFQs:

[('state','not in',('draft','sent'))]

Multiple conditions

  •  Either
    • Can be Purchased AND Can be Sold
  • OR
    • Landed Cost AND Service Type

As explained above, the OR symbol “|” and the AND symbol “&” come before the conditions. For both of them, one symbol can be followed by two conditions.

Here’s an example (from a Record Rule for Sales Order lines) which has more than two conditions:

"['&', '&', ('state', 'in', ['sale', 'done']), 
          ('is_service', '=', True),
         '|', ('project_id','!=', False), 
               ('task_id','!=', False)

It starts with two ANDs because there are three conditions:

  1. The SO line status is either sale or done.
  2. The SO line is for a service item (is_service =True)
  3. Either the project or task is specified

As you will see, this can become quite complex – but using Filters is a fairly easy way to try different combinations and eventually you should understand it!

Comparing with other fields

Domain can used to compare two fields rather than a field and a constant. For example, the Record Rule for “Sales / Own Documents only”:


Note that the first field name is in quotes, the second one is not.

The first comparison is whether the User ID on the sales order is the current user. The second is whether there is a User ID on the sales order. So this will be true if:

  • Sales order is for the current user OR
  • Sales order has no assigned salesperson (user_id is blank)

The comparison can be with a field in another Model:

[('partner_id', '=', commercial_partner_id)]

As mentioned above there are many other operators, for example:

[ '|',
    ('company_id', '=', False),
    ('company_id', 'in', company_ids)

This will select records with no company specified (meaning they are shared) OR records for the current companies (selected in the multi-company widget as explained here)

[('type', 'not in', ['sale', 'purchase', 'cash', 'bank'])]

This will select records where the type is not any of those specified (this example is a filter on Account Journals)

A problem entering domains

Note that there are some limitations with domains if you enter them through the user interface.

In the above examples:

  • Filters use a pop-up dialog box which makes it easier to enter domains, but at the cost of limiting the functionality that is available.
  • Domains for Record Rules are entered in raw form, which is a little more difficult but allows more options.

It is possible to change the widget in the XML.

This is an extract from the XML for Automated Actions (View Name is “Automations”)

<field name="filter_pre_domain"
         options="{'model': 'model_name', 'in_dialog': True}"
         attrs="{'invisible': [('trigger', 'not in',['on_write','on_create_or_write'])]}"/>

The change is simple: use the char_domain widget instead


Odoo ORM

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s