related 字段

Odoo related 字段为什么有时像“自动穿透权限”:related、related_sudo 与报表边界讲透

related 字段很方便,但很多人对它的默认权限语义理解并不完整。本文从 fields.py 默认值、测试模型与 read_group 测试讲清 related_sudo 为什么会影响展示、搜索与分组边界。

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

Odoo 开发里,related 字段非常顺手。

比如你在当前模型上想直接展示:

  • partner_id.name
  • move_id.company_id
  • order_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 是性能/检索层决策,不是安全或业务归属层的万能药。


最典型的几类场景:

比如薪资、私有联系人、受限财务对象、受控附件等。

如果你只是想让“有权的人看见,没权的人自然取不到”,那应该显式关掉 sudo 语义。

2. 这个字段会被拿去做筛选/分组/分析

尤其是在 list、pivot、graph、search panel 里使用时。

你需要确保这个 related 字段的值域对当前用户是真实且一致的。

3. 你不想让当前模型无意中成为“敏感数据投影面板”

有时当前模型本身权限很宽,但 related 指向的模型权限很窄。

这时 related_sudo=True 就容易把“窄权限数据”投影到“宽权限入口”上。


也不是说默认值都该关。

有些 related 字段本来就是为了稳定展示:

  • 单据上展示关联对象名称
  • 显示公司、币种、负责人这种低敏信息
  • 给用户一个更平滑的 UI 体验

这时默认 related_sudo 往往是合理的,因为你更在意:

  • 字段稳定可读
  • 不要因为路径中某跳读不到就界面忽空忽有

所以关键不在于“默认值好不好”,而在于:

你要把它当展示投影,还是当权限敏感的业务字段。


实战判断法

如果你在设计一个 related 字段,建议按这 4 个问题过一遍:

  1. 这个字段展示的是低敏信息,还是敏感信息?
  2. 它只用于表单显示,还是还要搜索/分组/统计?
  3. 当前模型的访问面,是否比目标模型更宽?
  4. 我希望它“尽量稳定显示”,还是“严格跟随用户可见边界”?

如果第 2、3、4 条偏安全或分析语义,就应该严肃考虑:

  • related_sudo=False
  • 是否真的要 store=True
  • 是否干脆改成显式 compute,并自己写更清楚的权限逻辑

一句话记住

如果只记一句,就记这句:

related 字段默认更偏“稳定投影”,不是天然等于“严格按当前用户边界取值”。

related_sudo 决定的,是这条投影在求值时到底有多“穿透”。

所以 related 最危险的地方,不是写法简单,而是它太容易让人忘记:

  • 展示语义
  • 权限语义
  • 报表语义

其实并不是一回事。

DISCUSSION

评论区

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