先把误区说清楚
很多人理解 related_sudo 时,会不自觉地脑补成这样:
- 这条 related 开了 sudo
- 那整条链以后都“带着 sudo 光环”
- 后面再 related 它,也会继续自动穿透
这其实说大了。
related_sudo 更准确的语义是:
它决定“当前这个 related 字段”在求值时要不要用 sudo。
它不是一个会沿着字段链无限传播的全局开关。
而 Odoo 官方在 test_orm 里专门放了一组很适合讲这个边界的例子:
foo_bar_idfoo_bar_sudo_idfoo_bar_id_namefoo_bar_sudo_id_name
这几个字段放在一起看,误区会非常容易拆开。
第一层:默认 related 字段为什么容易让人误会“全链 sudo”
在 odoo/orm/fields.py 里,related 字段初始化时会把:
compute_sudo = attrs.get('compute_sudo', attrs.get('related_sudo', True))
这说明 related 的默认倾向是:
- 如果你没显式关掉,当前 related 字段就按 sudo 语义求值
很多人看到这里,就自然延伸出一个错误推论:
- 那么所有依赖它的后续字段也会自动一起 sudo
源码并没有承诺这一点。
源码承诺的只是:
- 这个字段本身 怎么算
至于另一个字段再 related 到它,那已经是 另一个字段自己的定义问题。
第二层:为什么 foo_bar_sudo_id 和 foo_bar_sudo_id_name 特别适合看边界
test_orm 里有两条很值得对照的定义:
foo_bar_sudo_id = fields.Many2one(related='foo_id.bar_id', related_sudo=True)foo_bar_sudo_id_name = fields.Char(related='foo_bar_sudo_id.name', related_sudo=False)
这组定义说明什么?
说明官方自己就在测试一种非常典型的链式场景:
- 第一跳
foo_bar_sudo_id是 sudo 求值 - 第二跳
foo_bar_sudo_id_name又显式关掉 sudo
如果 related_sudo 真是“一开到底”,那第二个字段根本没必要再显式写 related_sudo=False。
但官方偏偏写了,恰恰说明:
- 每个 related 字段都在独立声明自己的求值边界
- 前一跳 sudo,不会替后一跳自动做决定
对照组:foo_bar_id 和 foo_bar_id_name
另一组定义是:
foo_bar_id = fields.Many2one(related='foo_id.bar_id', related_sudo=False)foo_bar_id_name = fields.Char(related='foo_bar_id.name', related_sudo=False)
这组更像“整条链都按非 sudo 语义走”。
跟上一组摆在一起,就能很清楚看出:
- related 的每一跳其实都像一个独立投影点
- 你不能因为前面有一跳 sudo,就默认后面所有投影也都自动越权
这也是链式 related 最值得建立的心智模型。
一个更准确的理解方式:每个 related 字段都是“新的投影面”
最容易理解错的地方,在于大家会把 related 路径当成一根连续水管。
但从字段定义角度看,更贴切的比喻是:
- 每个 related 字段都像在当前模型上新开了一个投影窗口
- 这个窗口用不用 sudo,要看它自己的定义
于是:
foo_bar_sudo_id是一个 sudo 窗口foo_bar_sudo_id_name又是另一个窗口,而且它自己选择了非 sudo
一旦用“窗口”而不是“水管”去理解,很多误判就会自然消失。
为什么这个边界很重要
如果你误以为 related_sudo 会整链传播,开发上就容易做出两类危险判断:
1)高估可见范围
以为前一跳能算出来,后一跳一定也能稳定拿到值。
2)低估泄露风险
以为只要上游 related 已经用过 sudo,后面就不用再显式审查安全边界。
这两种理解都不稳。
真正稳妥的做法是:
- 每定义一个 related 字段,就单独判断一次它该不该 sudo。
实战设计建议
如果你在项目里写链式 related,建议按这个顺序判断:
- 这一跳 related 是给 UI 稳定展示,还是给分析/过滤/权限敏感逻辑用
- 这一跳目标字段是不是敏感字段
- 当前模型的访问面是否比目标模型更宽
- 这一跳是否应该显式写
related_sudo=False
尤其是第二跳、第三跳 related,别偷懒沿用前一跳的想象。
源码没有替你做这个推断。
总结
related_sudo 最大的误区,不是“大家不知道它默认偏 sudo”,而是:
- 很多人知道它偏 sudo
- 却又进一步误以为它会沿整条 related 链自动传播
如果只记一句,就记这句:
related_sudo影响的是“当前 related 字段怎么求值”,不是“后续所有 related 都自动继承同一权限语义”。
把每一跳都当成独立投影面,你写出来的 related 链才会更安全,也更好解释。
DISCUSSION
评论区