Basic security mechanisms like Access Control List and Record Rules are not enough in many cases where custom business needs are involved while developing Odoo business applications. Condition-based access control, where security rules change according to the record's state, the current user's privileges, or a specific business process stage, is required in many cases.
In previous versions, such as Odoo 18, Odoo offered a handy but somewhat overlooked feature in the form of check_access_rule(), which enabled programmers to perform highly sophisticated security checks using Python at the ORM level.
But in the case of Odoo 19, the check_access_rule() function has been made obsolete with the introduction of the new check_access() function, although the functions of both are similar.
What is check_access() in Odoo 19?
The check_access() function is now the improved version of the ORM-based function that is responsible for applying record-level security at run-time in Odoo. Like its earlier version, it functions together with Access Control Lists (ACLs) and Record Rules to control access effectively. It is automatically executed by the ORM while performing actions like reading, writing, and deleting. Consequently, it is assured that there is no deviation from security rules irrespective of the access path, whether through the graphical user interface, REST API, or file importation.
Why Use check_access()?
This method is useful when:
- Security depends on record state
- Access is based on user roles
- Logic depends on business workflows
- Conditions combine multiple factors (user + record + state)
It is ideal for implementing complex logic that cannot be handled using XML record rules alone.
Example: Custom Security Rules for Sales Orders
Requirements:
- Prevent deletion of confirmed or completed orders
- Allow only Sales Managers to modify confirmed orders
from odoo import models
from odoo.exceptions import AccessError
class SaleOrder(models.Model):
_inherit = 'sale.order'
def check_access(self, operation):
# Always call super
super().check_access(operation)
# Prevent deletion of confirmed/done orders
if operation == 'unlink':
for order in self:
if order.state in ['sale', 'done']:
raise AccessError(
f"Cannot delete confirmed order {order.name}. Please cancel it first."
)
# Restrict editing of confirmed orders
if operation == 'write':
for order in self:
if order.state == 'sale':
if not self.env.user.has_group(
'sales_team.group_sale_manager'
):
raise AccessError(
f"Only Sales Managers can modify confirmed order {order.name}."
)
Explanation of the Logic
The use of super() will ensure that the default Access Control List (ACL) and record rules are honored and not circumvented. The deletion policy that uses the unlink method will prevent the deletion of orders that have already been confirmed or closed, since this would cause the loss of important data. Likewise, the role-based policy during the write method will restrict access to editing a confirmed order to only users who have Sales Manager rights. Failure to meet these policies results in the raising of an AccessError exception.
Use check_access() when:
- Security depends on record state
- Logic involves user roles + business conditions
- Rules must apply across all system entry points
- Record rules become too complex or insufficient
Best Practices
- Always call super().check_access(operation)
- Keep logic simple and efficient
- Avoid heavy queries inside the method
- Provide meaningful error messages
- Use only when necessary
In previous versions, such as Odoo 18, the check_access_rule() function served an important function in implementing dynamic, record-level security.
With the Odoo 19 version, this feature has been transformed into the check_access() function. Despite the change in name, the main purpose has remained constant—to serve as a robust tool for implementing complex condition-based security using the ORM model.
To read more about Complete Overview of Rare ORM Methods in Odoo 19, refer to our blog Complete Overview of Rare ORM Methods in Odoo 19.