related 最大的魅力,也是最大的误区
Odoo 开发里,related 字段非常顺手。
比如你在当前模型上想直接展示:
partner_id.namemove_id.company_idorder_id.user_id
很多人第一反应就是:
partner_name = fields.Char(related='partner_id.name')
写起来很优雅。
但真正的问题在于:
这个 related 字段到底用谁的权限去算?它会不会把你本来不该看到的数据“投影”出来?
答案在源码里非常明确,而且很多人第一次看会有点意外:
- related 字段默认
store=False - 默认
readonly=True - 默认
copy=False - 更关键的是:默认会以 superuser 语义计算,也就是
related_sudo=True的效果
这一点在 odoo/orm/fields.py 的属性初始化逻辑里能直接看到:
- 若字段有
related compute_sudo = attrs.get('compute_sudo', attrs.get('related_sudo', True))
也就是说,如果你不显式改,related 字段天然倾向于用 sudo 计算。
为什么官方会这样设计
因为 related 字段本质上不是“独立业务字段”,而是:
把另一条字段路径投影到当前模型上。
如果 related 每跳都严格按当前用户权限中途截断,那么大量界面显示会变得很不稳定:
- 同一张单据有的人能看到投影值,有的人变空
- 很多技术性展示字段会失去一致性
- 依赖 related 的显示逻辑也会变得很碎
所以官方默认选择了一个更偏“展示可用性”的立场:
- 先把 related 当作一个可稳定求值的投影字段
- 如果你需要更严格的安全边界,再显式设
related_sudo=False
这是一种非常典型的框架折中。
但“能算出来”不等于“安全边界不存在”
很多人看到 related 默认 sudo 后,会误解成:
- 那 related 就能绕过一切权限
这又走到另一个极端了。
正确理解应该是:
1. 计算语义可能是 sudo
也就是这条 related 路径在取值时,默认不容易因为中间字段权限而断掉。
2. 但它仍然处在当前模型字段体系里
你是否能看到它、能不能把它用于某些操作,还会受到:
- 当前模型字段访问
- 视图字段 groups
- 具体 read / web_read / read_group 的实现边界
- comodel 的 record rule
等因素影响。
这也是为什么官方测试里会专门覆盖:
related_sudo=False的 related 字段related_sudo=True也不能绕过 comodel record rule 的情况
也就是说:
related_sudo 影响的是“求值语义”,不是“给你一张万能通行证”。
为什么它会影响报表与 group by
很多人第一次踩 related_sudo 的坑,不是在表单,而是在报表或 list 分组。
官方 test_read_group 的测试注释里有两个特别值得记住的点:
foo_id_name因为related_sudo=False,不能安全地用于 group by- 即使
related_sudo=True,也不该绕过 comodel 的 ir.rule
这说明 Odoo 在报表场景里的态度很谨慎:
- 分组、聚合不是简单“把字段值显示一下”
- 它会把 related 字段扩展成查询语义、权限语义和结果集合语义
一旦 related 路径牵涉到用户对目标模型记录的可见性差异,group by 结果就可能失真,或者出现安全边界问题。
所以 related 字段用来“展示”往往没问题;但一旦你想把它拿去:
- 搜索
- 过滤
- 分组
- 聚合
- 做分析视图主维度
你就必须重新审视它的权限语义是不是稳。
store=True 不会自动把它变成“业务真相字段”
这是另一个很常见的误解。
有人会觉得:
- non-stored related 只是实时映射
- 那我加
store=True不就稳定了吗
store=True 确实会改变很多事情:
- 可以减少读取时的路径计算
- 更适合搜索/分组的一些场景
- 能把结果落到当前模型表里
但它没有改变这个字段的本质:
它仍然是别处数据的投影,不是这个模型独立拥有的业务事实。
这意味着:
- 源字段变了,它要重新同步
- 权限语义设计错了,落库也只是把问题固化
- 拿它做报表主轴时,仍要考虑是否把本来不该被看见的信息投影到了更宽的范围
所以 store=True 是性能/检索层决策,不是安全或业务归属层的万能药。
什么时候该显式写 related_sudo=False
最典型的几类场景:
1. related 路径跨到了敏感模型
比如薪资、私有联系人、受限财务对象、受控附件等。
如果你只是想让“有权的人看见,没权的人自然取不到”,那应该显式关掉 sudo 语义。
2. 这个字段会被拿去做筛选/分组/分析
尤其是在 list、pivot、graph、search panel 里使用时。
你需要确保这个 related 字段的值域对当前用户是真实且一致的。
3. 你不想让当前模型无意中成为“敏感数据投影面板”
有时当前模型本身权限很宽,但 related 指向的模型权限很窄。
这时 related_sudo=True 就容易把“窄权限数据”投影到“宽权限入口”上。
什么时候默认 related_sudo 反而是合理的
也不是说默认值都该关。
有些 related 字段本来就是为了稳定展示:
- 单据上展示关联对象名称
- 显示公司、币种、负责人这种低敏信息
- 给用户一个更平滑的 UI 体验
这时默认 related_sudo 往往是合理的,因为你更在意:
- 字段稳定可读
- 不要因为路径中某跳读不到就界面忽空忽有
所以关键不在于“默认值好不好”,而在于:
你要把它当展示投影,还是当权限敏感的业务字段。
实战判断法
如果你在设计一个 related 字段,建议按这 4 个问题过一遍:
- 这个字段展示的是低敏信息,还是敏感信息?
- 它只用于表单显示,还是还要搜索/分组/统计?
- 当前模型的访问面,是否比目标模型更宽?
- 我希望它“尽量稳定显示”,还是“严格跟随用户可见边界”?
如果第 2、3、4 条偏安全或分析语义,就应该严肃考虑:
related_sudo=False- 是否真的要
store=True - 是否干脆改成显式 compute,并自己写更清楚的权限逻辑
一句话记住
如果只记一句,就记这句:
related 字段默认更偏“稳定投影”,不是天然等于“严格按当前用户边界取值”。
而 related_sudo 决定的,是这条投影在求值时到底有多“穿透”。
所以 related 最危险的地方,不是写法简单,而是它太容易让人忘记:
- 展示语义
- 权限语义
- 报表语义
其实并不是一回事。
DISCUSSION
评论区