先说结论
很多人把 Odoo 记录规则总结成一句话:
search 时系统会自动给你拼上 ir.rule 的 domain。
这句话只够应付入门,不够应付真实开发。
更完整的说法应该是:
- 先看模型级权限
ir.model.access - 再根据当前操作计算记录规则 domain
- 再对当前记录集做允许/不允许判断
- 最终得到“这批记录里你到底能对哪些做 read / write / unlink”
所以真正危险的误解是:
- 以为“能 search 到”就一定能写
- 以为“count 有数字”就说明前端读详情不会报错
- 以为
sudo()只是“让 search 更全一点”
其实这些都把权限边界想简单了。
新版源码视角:访问检查不是一个动作,而是一条链
在 /home/ubuntu/odoo-temp/odoo/orm/models.py 里,新版核心逻辑已经把旧的 check_access_rights()、check_access_rule()、_filter_access_rules_python() 都收束到新的访问检查模型上。
最关键的方法是:
check_access()has_access()_filtered_access()_check_access()
其中 _check_access() 的顺序非常值得记住。
第一层:模型权限先拦
源码先调用 ir.model.access 检查当前模型在当前操作上是否允许。
如果模型级权限都没有:
- 不是“返回空记录集”
- 而是压根不允许你继续讨论具体哪些记录能不能碰
这就是为什么有些问题根本与记录规则无关,而是 ACL 没开。
第二层:记录规则 domain 不是“装饰”,而是真正参与允许记录计算
当模型权限通过后,源码会算出:
Rule._compute_domain(self._name, operation)
然后它不是直接说“以后 search 都加这个 domain 就完了”,而是进一步做:
- 对当前记录集
self - 用
sudo().with_context(active_test=False).filtered_domain(domain) - 算出这批记录里哪些满足当前规则
最后把不满足规则的记录视作 forbidden。
这段实现特别说明两件事:
- 记录规则最终要回答的是“你能不能操作这批真实记录”
- 它不是前端检索体验功能,而是安全边界的一部分
为什么会出现“列表里有,点进去报错”的错觉
现实项目里,最常见的抱怨是:
- 列表里明明看到了
- 点详情却报权限错误
- 或者批量操作时只有部分记录失败
这类现象背后,通常有几种可能:
1. 列表和详情走的模型/上下文不同
例如列表页的 domain、公司上下文、active_test、关联字段展开方式不同。
2. 你看到的是上游记录,但真正报错的是下游关联记录
比如订单本身能读,但它的某个关联发票、公司、仓库记录规则更严。
3. 自定义代码提前 sudo() 查出记录,后面又在非 sudo 环境里继续写
这时开发者以为“我都拿到记录了”,但真正写入时还是会重新触发访问检查。
所以“看得到”从来不等于“所有后续动作都过得去”。
sudo() 真正绕过了什么,没绕过什么
很多人一遇到权限问题就先加 sudo(),然后把问题暂时压下去。
但从边界上说,sudo() 的意义不是“多拿几条数据”,而是:
切换到超级用户语义下执行,绕过原本属于当前用户的访问限制。
因此它会带来两个风险:
风险 1:你修的不是问题,是症状
如果本来应该通过规则修正业务边界,却直接 sudo(),你只是把权限设计问题藏起来了。
风险 2:你可能把本不该暴露的数据带回普通用户界面
比如:
- 用
sudo()读出不该见的记录 - 再把结果写到字段、统计、名称搜索或页面计数里
这类 bug 最难排,因为它看起来“没有报错”,但实际上已经越权泄露。
多公司场景下为什么更容易误判
记录规则和多公司叠加时,最容易出现“我明明没改权限,为什么突然不能看”的情况。
原因通常是:
- 当前公司切换了
allowed_company_ids变化了- 某些关联记录
check_company失败 - 规则 domain 里含公司条件,而你的上下文没跟上
于是开发者经常误以为:
- 是 search domain 写错了
- 是缓存问题
- 是前端没刷新
实际上很可能只是当前环境下允许访问的记录集已经变了。
开发时最稳的排查顺序
当你怀疑是记录规则导致异常时,建议按这个顺序查:
- 当前用户对模型是否有 ACL 权限
- 当前操作是
read、write还是unlink - 当前上下文里的公司、
active_test、sudo 状态是什么 - 目标记录是否真的落在规则 domain 内
- 是否是某个关联模型先报错,而不是当前模型本身
这个顺序比“上来就改规则”稳得多。
一个常被忽略的事实:安全边界是按“操作”算的
同一条记录:
- 可能允许
read - 但不允许
write - 也可能允许
write - 却不允许
unlink
因为规则计算和权限判断都带着 operation 语义。
所以不要把“这个用户有这条记录权限”理解成一个单值判断。
更准确地说,应该理解成:
这个用户对这条记录,在这个操作下,是否被允许。
最后的判断句
在 Odoo 里,记录规则从来都不是“search 自动拼 domain”这么简单。
它的本质更接近:
模型权限 + 记录规则 + 当前上下文 + 当前操作,共同决定这批记录里哪些真的可访问。
理解了这一点,你就不会再把:
- 看得到
- 搜得到
- 计数对得上
- 偶尔能写
误当成“权限已经没问题”。
真正稳定的权限开发,靠的不是多写几个 sudo(),而是把访问边界先想清楚。
DISCUSSION
评论区