这篇只回答一个调试现场问题
很多人第一次排查 ir.rule 时,都会做一个看似合理的动作:
- 切到 shell
sudo()一下模型- 跑
search()或_compute_domain() - 然后得出结论:这条记录规则根本没生效
问题是,这个结论经常从第一步就已经歪了。
因为在 Odoo 源码里,sudo() 不是“更方便地观察规则”,而常常是“直接不再参与规则”。
这篇不再泛讲 domain_force 怎样 AND / OR 拼装,而只讲一个最容易造成调试幻觉的点:
sudo时_get_rules()为什么会直接返回空规则集allowed_company_ids为什么又会让结果看起来像“忽然变了”
_get_rules() 最关键的分支:self.env.su
在 odoo/addons/base/models/ir_rule.py 里,_get_rules() 很早就有一个判断:
- 如果
self.env.su - 直接返回空的
ir.rulerecordset
这意味着什么?
意味着你一旦在 superuser 环境里测试:
- 不是“拿到所有规则”
- 不是“规则照算,只是权限更高”
- 而是这层规则压根不参与计算
所以很多“我在 sudo 下能看到全部记录,所以 rule 没生效”的判断,本身就没有测试到 rule。
你看到的不是规则结果,而是绕过规则后的结果。
为什么这个设计其实很合理
ir.rule 本来就是针对普通访问路径加的安全边界。
如果 superuser 还继续被记录规则约束,会出现很多维护问题:
- 管理操作被自己配置的 rule 卡住
- 数据修复工具无法越过业务限制
- 系统级行为和普通用户行为混在一起
所以 Odoo 的态度非常明确:
sudo是绕过安全边界,不是观察安全边界。
也正因为如此,调试记录规则时最忌讳的就是拿 sudo 结果当证据。
_compute_domain() 为什么又让人觉得“像在生效”
另一个容易误导人的地方是 _compute_domain() 带缓存。
源码里它使用 ormcache,而缓存键包含:
uidsumodel_namemodetuple(self._compute_domain_context_values())
而 _compute_domain_keys() 默认返回的上下文键里,最重要的是:
allowed_company_ids
这意味着同一个模型、同一个用户、同一个操作,只要你变了下面任意一个条件,缓存结果都可能不同:
- 有没有 sudo
- 激活公司集合是不是换了
- 读写删模式是不是变了
如果你没意识到这些键参与缓存,就很容易产生一种错觉:
- 刚才还像没规则
- 怎么切个公司又有规则了
- 或者刚才像有规则,为什么下一次又没了
其实很多时候不是“规则随机失灵”,而是 你换了缓存命中条件。
allowed_company_ids 为什么特别重要
在多公司环境里,记录规则经常会写:
company_id in company_ids- 或其他依赖当前激活公司集合的判断
而 _eval_context() 提供给规则求值的 company_ids,本身就来自当前环境中的激活公司。
所以切公司不只是前端体验变化,而是:
- 规则求值上下文变了
_compute_domain()缓存键也变了
这正是很多“同一个用户同一条 rule,看起来像时灵时不灵”的根源。
最常见的 4 个调试误判
1)用 sudo() 测完后说 rule 没生效
这最常见,也最不靠谱。因为你可能根本没让 rule 参与。
2)切公司后结果变化,就怀疑缓存脏了
很多时候不是缓存脏,而是缓存键本来就包含 allowed_company_ids。
3)只测 read,却拿去解释 write 或 unlink
_get_rules() 和 _compute_domain() 都按 mode 分开算。不同操作,命中的规则可能不同。
4)把“规则为空”理解成“没有配置规则”
不一定。也可能只是你在 su 环境里。
更靠谱的调试顺序
如果你真想判断一条记录规则有没有生效,建议按这个顺序:
- 先确认自己是不是 sudo 环境
- 确认当前 mode 是 read、write、create 还是 unlink
- 确认当前激活公司集合
allowed_company_ids - 再看
_get_rules()挑出了哪些规则 - 最后才看
_compute_domain()的组合结果
这个顺序的价值在于:
- 先判断自己是不是在测规则
- 再判断测的是哪一套上下文
而不是一上来就把 domain 结果当最终答案。
什么时候 sudo 仍然有用
这并不是说 sudo 毫无价值。
它仍然适合做两类事:
- 对照查看“绕过规则后”的完整数据集
- 修复或迁移那些本来就需要越权处理的数据
但如果你的目标是:
- 证明某条 rule 生效没
- 解释普通用户为什么看不到某些记录
那 sudo 更像对照组,不是主测试方法。
总结
调试 ir.rule 时,最容易踩的坑不是 domain 写错,而是测试姿势错了。
如果只记一句,就记这句:
在 Odoo 里,
sudo()常常不是“更清楚地看记录规则”,而是“直接退出记录规则语境”。
再加上一层 allowed_company_ids 参与缓存,你就会明白为什么很多权限问题看起来像玄学,其实只是:
- 你换了安全上下文
- 你又把绕过规则的结果当成了规则结果
把这两层先捋清,ir.rule 的调试会容易很多。
DISCUSSION
评论区