先说结论
很多人第一次接触 Odoo 权限时,会把三件事混在一起:
- 菜单能不能看到
- 模型能不能操作
- 记录能不能读写
其实它们不是一回事。
其中,ir.rule 管的是 记录级别的数据边界。它不是菜单权限,也不是简单的“这个模型能不能访问”,而是:
即使你已经能访问这个模型,你还只能看到规则允许的那些记录。
ir.rule 是怎么定义的
在 odoo/addons/base/models/ir_rule.py 里,核心字段很直接:
model_idgroupsdomain_forceperm_read/perm_write/perm_create/perm_unlink
这说明记录规则至少回答四个问题:
- 适用于哪个模型
- 适用于哪些组
- 用什么 domain 限制记录
- 在哪种操作上生效
所以 ir.rule 不是单一开关,而是一个“按操作、按组、按记录筛选”的组合体。
先拿规则,再算最终 domain
真正生效的关键方法是 _compute_domain()。
它会先做几件事:
- 找出当前模型的相关规则
- 把父模型
inherit进去的规则也纳入 - 区分 global rule 和 group rule
- 把最终 domain 组合出来
这里最容易搞错的一点是:
global rule 和 group rule 的关系不是同一种逻辑
- global rule:多个规则是 AND 逻辑
- group rule:多个规则是 OR 逻辑
最后整体再做 AND。
换句话说:
全局规则负责“底线”,组规则负责“通道”。
这就是为什么有时你明明给了一个组权限,还是看不到某些记录——因为全局规则先把门关上了。
domain_force 不是“写了就完事”
domain_force 会被 safe_eval 求值,然后再转成 Domain。
这意味着它不是普通文本,而是会进入真实求值和校验流程。
源码里还有 _check_domain():
- 如果规则启用且有 domain
- 就先求值
- 再验证 domain 是否对目标模型合法
所以写规则的时候,不能只想着“表达式对不对”,还要想着:
- 目标模型有没有这些字段
- 当前用户上下文会不会影响结果
- 多公司条件有没有被正确纳入
_get_rules() 和 _get_failing() 分别做什么
_get_rules() 负责从数据库里找出当前用户能参与计算的规则。
它会看:
- 规则是否 active
- 是否支持当前操作模式
- 是否属于 global rule
- 或者是否属于当前用户所在的组
而 _get_failing() 是用来构造报错信息的。
当你遇到 AccessError 时,它会帮你定位:
- 哪些规则在失败
- 哪些记录被挡住了
- 如果是多公司问题,是否可以通过切换公司解决
这就是为什么 Odoo 的权限报错有时候会比传统 ERP 友好很多。
最容易误解的 3 件事
1)“我能进菜单”不代表“我能看记录”
菜单权限和记录规则不是一层。
2)“给了组权限”不代表“规则一定放行”
组规则只是 OR 逻辑的一部分,global rule 仍然会继续限制。
3)sudo() 不是权限修复器
sudo() 只是绕过当前用户规则执行代码,不代表真实数据边界消失了。
如果你的业务逻辑依赖 sudo(),最后还是要回头检查 record rule 是否设计合理。
开发和排错建议
如果你在调试数据看不到、写不进去、删不掉,建议按这个顺序看:
- 先确认是不是 ACL(模型访问权)问题
- 再看是不是
ir.rule的记录级限制 - 再看是不是多公司 domain 导致的过滤
- 最后才考虑是不是代码里用了
sudo()或自定义 domain
一句话总结:
ACL 决定你能不能碰这个模型,record rule 决定你能碰哪些记录。
这就是 ir.rule 最重要的边界感。
DISCUSSION
评论区