Skip to content

Odoo glossary

Record rules

Also called ir.rule, record rule, Odoo record rules

Definition

Record rules are ir.rule records that limit which individual records of an Odoo model a user can read, write, create or delete. Each rule holds a domain that is checked record by record after access rights, so record rules can narrow what ir.model.access.csv grants but never go beyond it.

An access line answers “can this user work with sale orders at all?” A record rule answers the next question: which sale orders. The standard sale module is a good model to copy. A salesperson in the “own documents” group sees orders where they’re the salesperson or none is set, a salesperson with “all documents” sees everything, and a company rule sits over both.

The fields that matter

  • model_id is the model the rule applies to.
  • domain_force is a domain, written as a Python expression. If a record matches, the operation is allowed. Inside it you can use user (the current user as a record), company_id (the active company’s id), company_ids (a list of ids for every company the user can access) and time.
  • groups decides the rule type. With no groups it’s a global rule. With one or more groups it’s a group rule.
  • perm_read, perm_write, perm_create, perm_unlink mean something different from the access CSV. Here they pick which operations the rule is checked for, and they’re all on by default. A rule with perm_read off doesn’t stop anyone reading.

How global and group rules combine

This is the part to get right, because the two types behave in opposite ways.

Global rules intersect. If two global rules apply, a record has to satisfy both. Every global rule you add can only take access away.

Group rules unify. Of the rules attached to the user’s groups, a record only needs to satisfy one. Adding a group rule can widen what a user sees, but only within the limits the global rules set.

Then the two sets intersect with each other. So a manager whose group has an “all records” rule still can’t see another company’s data if a global company rule exists.

And if no rule applies to a model and operation, access is granted. Rules are default-allow, which is why a loose access line with no rules behind it is a real leak.

An example

Bookings belong to a company and a user. Regular users see their own, managers see all of them, and no one sees another company’s bookings:

<odoo>
    <record id="equipment_rental_company_rule" model="ir.rule">
        <field name="name">Equipment rental: multi-company</field>
        <field name="model_id" ref="model_equipment_rental"/>
        <field name="domain_force">[('company_id', 'in', company_ids)]</field>
    </record>

    <record id="equipment_rental_own_rule" model="ir.rule">
        <field name="name">Equipment rental: own bookings</field>
        <field name="model_id" ref="model_equipment_rental"/>
        <field name="groups" eval="[Command.link(ref('group_rental_user'))]"/>
        <field name="domain_force">['|', ('user_id', '=', user.id), ('user_id', '=', False)]</field>
    </record>

    <record id="equipment_rental_all_rule" model="ir.rule">
        <field name="name">Equipment rental: all bookings</field>
        <field name="model_id" ref="model_equipment_rental"/>
        <field name="groups" eval="[Command.link(ref('group_rental_manager'))]"/>
        <field name="domain_force">[(1, '=', 1)]</field>
    </record>
</odoo>

The company rule has no groups, so it’s global. If the manager group implies the user group, a manager matches both group rules, and because group rules unify the (1, '=', 1) rule wins. This XML works unchanged on Odoo 17, 18 and 19. The sale module loads its rules inside <odoo noupdate="1">, which keeps an admin’s edits safe from module updates but also means later changes to your XML won’t reach an existing database on -u.

Where rules get bypassed or misused

sudo() ignores them. So does raw SQL, since rules are enforced by the ORM. A controller that runs a search with sudo() hands back every record, whatever the rules say.

Making the company rule a group rule. Odoo’s own docs warn about this. A multi-company rule attached to a group can be widened by any other group rule the user has, so keep it global.

Stacking global rules. Two global rules with no overlap in what they match remove all access to the model, for every user who isn’t in superuser mode.

Restricting with a group rule alone. Putting a narrow rule on the user group does nothing for someone who also holds a group with a wider rule.

On a multi-company database, rules like the ones above belong in the addon next to the access lines, as module code that gets reviewed, rather than as records someone edited by hand in the settings. Odoo module development describes the full output, and Odoo customization covers changes to standard apps.

Sources

The official documentation and source code this page was checked against.

  1. 01 Security in Odoo: Record Rules, Odoo 19 documentation odoo.com
  2. 02 Restrict access to data, Odoo 19 developer tutorial odoo.com
  3. 03 addons/sale/security/ir_rules.xml (19.0), odoo/odoo on GitHub github.com

Where Record rules comes up

Terms used on this page

More Odoo terms

Your next module is one paragraph away

Write it the way you’d explain it to a new hire. We’ll turn it into an app you can read, test and install.

Odoo 19

Create your account

Free to start. No card needed.

By signing up you agree to our terms and privacy policy.