Domain is used in Odoo to select records from a Model (database table) – in many different places:
- Menus / Windows Actions
- Form Views – to select records from a one2many or many2many
- Record Rules
- Filters (see below)
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:

[["partner_invoice_id.state_id","=","California"]]

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 “|” :

["|", ["purchase_ok","=",True], ["sale_ok","=",True] ]
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.
Examples
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

["|", "&", ["purchase_ok","=",True], ["sale_ok","=",True], "&", ["landed_cost_ok","=",True], ["type","=","service"] ]
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:
- The SO line status is either
sale
ordone
. - The SO line is for a service item (
is_service
=True
) - 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”:
['|',
('user_id','=',user.id),
('user_id','=',False)]
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"
widget="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
I’m curious what would be the best practice of the following use case:
People have access to the Accounting budget view they are responsible for and the corresponding analytic accounts are accessible to them but they shouldn’t be able to view other budgets and analytic accounts (not just invisible but complete denial of access via the Odoo error window).
The split in the budget is easy since you can work with the responsible user id and a domain record rule for Budget like [‘|’,
(‘user_id’,’=’,user.id),
(‘user_id’,’=’,False)
]
But limiting a specific analytic account to a specific user is hard to figure out, at least for me.
LikeLike
Anybody knows how to limit team members to view Helpdesk tickets of their own team, regardless if member is assigned to the ticket?
LikeLike