先说结论
很多 Odoo 开发里最危险的误区之一,就是把 sudo()、with_user()、with_company() 都当成“切个环境继续跑”。
它们确实都会改变执行上下文,但改的层面根本不是一回事:
sudo():改变安全边界with_user():改变当前用户身份视角with_company():改变公司上下文
这三个如果混着用却不分清,会非常容易出现:
- 权限绕过
- 多公司取值异常
- 看起来偶发、其实边界错位的 bug
为什么这三个特别容易被混
因为它们都长得像“我想换个环境执行一下”。
于是很多人脑子里只剩一句模糊的话:
- 换一下上下文就好了
但真正问题在于,它们各自改变的是完全不同的维度。
如果你不区分“权限是谁的”“身份是谁的”“公司是哪家的”,后面排查会非常痛苦。
sudo() 真正改变了什么
sudo() 最核心的意义不是“换用户”,而是:
绕开普通权限边界,让操作以更高权限执行。
这通常意味着:
- access rights 检查可能被绕过
- record rules 边界可能不同
- 某些普通用户做不到的底层动作可以继续推进
所以 sudo() 的危险点就在于,它很容易让你在不知不觉中扩大可操作范围。
with_user() 真正改变了什么
with_user() 更接近:
让这段逻辑以“某个具体用户的身份视角”运行。
也就是说,它强调的是“像这个用户一样看和做”。
这和 sudo() 不是一回事。
如果你想模拟某个用户真实能看到什么、能操作什么,with_user() 才更贴近语义。
而不是一上来就 sudo()。
with_company() 又在改变什么
with_company() 关心的不是权限,也不是身份本身,而是:
当前逻辑应当以哪个公司上下文来解释字段、规则与业务环境。
这在多公司场景里非常关键。
因为很多东西会受公司上下文影响:
- 公司相关默认值
- company_dependent 字段
- 某些规则与取值
- 业务配置解释方式
所以它处理的是“在哪家公司语境里运行”,不是“谁有权限”。
为什么 sudo().with_company(...) 经常一起出现
因为它们处理的是不同维度,所以确实可能同时需要。
比如某些系统流程要:
- 以系统权限推进底层动作
- 但又必须在正确公司上下文里创建对象
这时就可能会看到类似组合。
但“常一起出现”不等于“总是一起用”。
真正该不该组合,还是取决于:
- 你要切的是权限、身份、还是公司语境
一个特别实用的判断方法
遇到这类 API 选择题时,先问自己:
1. 我是想提权,还是只是想模拟某个用户?
提权更接近 sudo();模拟用户更接近 with_user()。
2. 问题是权限边界,还是公司语境?
如果是公司维度解释不同,更接近 with_company()。
3. 用户可见结果是否也应该被提权后的范围影响?
这一步很关键,很多越权泄露就出在这里。
实战里最容易踩的 5 个坑
1. 本来只是想模拟用户,却直接 sudo()
结果把权限问题藏起来了。
2. 明明是多公司上下文问题,却一直查 group 和 access
方向就查偏了。
3. 提权执行后,把不该暴露的结果返回给前端
这是非常危险的边界错误。
4. 以为 with_company() 会自动解决权限问题
它不会。
5. 三者混着链式调用,却没想清每一步在改什么
这类代码后期最难维护。
一句话记忆法
把它们记成一句话:
sudo()是提权,with_user()是换身份视角,with_company()是换公司语境;它们都能改上下文,但改的根本不是同一层。
理解这一句,很多 Odoo 权限和多公司问题就没那么容易看混。
DISCUSSION
评论区