记录规则机制

Odoo 记录规则如何真正生效:ir.rule、域表达式与多公司过滤

从 `ir.rule` 源码看记录规则如何把 `domain_force`、用户组和 `allowed_company_ids` 组合成最终访问域。

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

先把两个概念分开

很多人第一次看 Odoo 权限时,会把 访问权限记录规则 混在一起。 其实它们不是一回事:

  • 访问权限(access rights) 解决的是“这个用户能不能碰这个模型”
  • 记录规则(record rules) 解决的是“这个用户能碰这个模型里的哪些记录”

前者像大门钥匙,后者像门禁卡。 你有大门钥匙,不代表所有房间都能进去。

ir.rule 里最重要的三个输入

odoo/addons/base/models/ir_rule.py 里,规则计算的核心输入有三个:

  1. domain_force:规则本身的过滤条件
  2. groups:规则是否只对某些用户组生效
  3. allowed_company_ids:当前激活公司的上下文

相关代码先从 _get_rules() 开始,按模型、操作类型和用户组把规则挑出来;然后 _compute_domain() 再把它们拼成最终域。

_get_rules():先筛选出“和你有关”的规则

_get_rules() 的作用是按当前用户和操作类型取出规则:

  • 不是当前模型的规则不要
  • 没启用的规则不要
  • 不支持当前操作模式的规则不要
  • 和用户组不匹配的组规则不要

你可以把它理解成“先做候选集,再谈合并”。

这一步非常关键,因为它决定了后面的域到底来自哪些规则。

_compute_domain():全局规则要 AND,组规则要 OR

这是最容易看错的一段。

源码的思路是:

  • 全局规则(global rules):全部都要满足,所以是 AND
  • 组规则(group rules):同一组里的候选规则会被 OR 起来

也就是说,Odoo 不是简单把所有 domain 拼一起,而是在做有层次的组合。

这会直接影响你的调试结论:

  • 如果记录“明明应该可见却看不到”,可能是某条全局规则太严
  • 如果某个组的用户能看到的记录太多,可能是组规则被 OR 后放宽了

多公司为什么要特别小心

ir.rule_eval_context() 里会把下面这些值放进安全评估上下文:

  • user
  • company_ids
  • company_id

同时,_compute_domain_keys() 又把 allowed_company_ids 放进缓存键。

这意味着:

  • 你切换公司,规则域可能变
  • 规则结果也会跟着缓存重新计算

所以在多公司环境里,很多“权限问题”其实不是 ACL 出错,而是规则域在当前公司上下文下把记录过滤掉了。

_make_access_error() 为什么这么“贴心”

Odoo 抛出拒绝访问时,不只是说一句“没权限”。 _make_access_error() 会尽量帮助你定位问题:

  • 告诉你是 read / write / create / unlink 哪种操作失败
  • 提示是哪一个模型
  • 如果是内部用户,还会列出可能导致失败的规则
  • 如果涉及公司,还会提示可能需要切换公司

这不是“文案好看”这么简单,而是 Odoo 试图把权限调试成本降下来。

新手最容易踩的三个坑

  1. 只看 ir.model.access.csv,不看 ir.rule 模型权限允许,不代表记录一定可见。

  2. 把 global rule 当成“默认规则” 它不是默认值,而是硬约束;所有全局规则都要同时满足。

  3. 忽略公司上下文 很多规则里都隐含了 company_id 或可用公司集合。

开发和排错时的实用建议

  • 先确认是 ACL 失败,还是 record rule 失败
  • 再看具体操作是 read、write 还是 create
  • 如果是多公司场景,先切换上下文再复现
  • 调试自定义规则时,优先检查 domain_force 是否能在当前用户上下文下通过 safe_eval

一句话总结

Odoo 记录规则的本质,是把“谁能看什么”转换成一个最终访问域。 这个域由规则类型、用户组和多公司上下文共同决定,而不是靠单条规则简单生效。

DISCUSSION

评论区

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