记录规则边界

Odoo 记录规则不是菜单权限:ir.rule 的真实生效顺序

解释 global rule、group rule、domain_force 和 _compute_domain 的组合方式,理解为什么同一个模型不同用户会看到不同的数据边界。

Odoo 开发 框架
进阶 开发者 1 分钟阅读
0 评论 0 点赞 0 收藏 6 阅读

先说结论

很多人第一次接触 Odoo 权限时,会把三件事混在一起:

  • 菜单能不能看到
  • 模型能不能操作
  • 记录能不能读写

其实它们不是一回事。

其中,ir.rule 管的是 记录级别的数据边界。它不是菜单权限,也不是简单的“这个模型能不能访问”,而是:

即使你已经能访问这个模型,你还只能看到规则允许的那些记录。


ir.rule 是怎么定义的

odoo/addons/base/models/ir_rule.py 里,核心字段很直接:

  • model_id
  • groups
  • domain_force
  • perm_read / perm_write / perm_create / perm_unlink

这说明记录规则至少回答四个问题:

  1. 适用于哪个模型
  2. 适用于哪些组
  3. 用什么 domain 限制记录
  4. 在哪种操作上生效

所以 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 是否设计合理。


开发和排错建议

如果你在调试数据看不到、写不进去、删不掉,建议按这个顺序看:

  1. 先确认是不是 ACL(模型访问权)问题
  2. 再看是不是 ir.rule 的记录级限制
  3. 再看是不是多公司 domain 导致的过滤
  4. 最后才考虑是不是代码里用了 sudo() 或自定义 domain

一句话总结:

ACL 决定你能不能碰这个模型,record rule 决定你能碰哪些记录。

这就是 ir.rule 最重要的边界感。

DISCUSSION

评论区

想参与讨论?先 登录 再发表评论。
还没有评论,你可以成为第一个留言的人。