Odoo 权限问题里,最让人烦的不是“完全没权限”,而是这种半吊子状态:
- 列表能看到;
- 详情能打开;
- 但一保存就报错;
- 或者能改自己那几条,改别人的就不行。
很多人这时会说一句:
不是都给权限了吗?
问题就在这里。Odoo 里的“给权限”至少分两层:
- ACL(ir.model.access):这个模型能不能做
read/create/write/unlink; - 记录规则(ir.rule):这个用户对“哪些记录”能做这些操作。
从 /home/ubuntu/odoo-temp/odoo/orm/models.py 的 _check_access() 看,Odoo 的判断顺序其实很直接,但也很容易被误解。
一、先过 ACL,再过记录规则
_check_access() 的源码先调:
ir.model.access.check(model, operation)
如果模型级别都没给 write,那根本不会继续往下。
只有模型操作被允许,Odoo 才会继续算记录规则 domain:
Rule._compute_domain(self._name, operation)
然后再拿当前记录集去套这个 domain。
这意味着:
- ACL 决定“这个动作是否原则上允许”;
- 记录规则决定“这批记录里哪些真的允许”。
所以“能读不能写”并不奇怪,因为 read 和 write 本来就是两套独立判断。
二、为什么“能看到”不等于“能修改”
很多新手默认以为:
- 看得到 = 肯定能改
这在 Odoo 里完全不成立。
因为记录规则本来就是按操作类型算 domain 的。也就是说,同一条记录可能出现这四种组合:
- 能读,不能写;
- 能读,能写;
- 能写(通常也能读),但不能删;
- 能创建,但创建后不一定还能改全部字段关联的数据。
尤其是业务里常见这种规则:
- 所有人能看本部门记录;
- 只有负责人能改;
- 只有经理能删。
这不是系统不一致,而是权限模型本来就这么设计。
三、记录规则是对“记录集合”做筛选,不是对按钮做装饰
_check_access() 很值得注意的一句是:
- 先算 domain;
- 再用
self.sudo().with_context(active_test=False).filtered_domain(domain)过滤允许记录; - 最后把禁止的那部分记录拿出来报错。
这说明记录规则的本质不是“页面上某个按钮灰不灰”,而是:
这次操作提交到 ORM 时,当前 recordset 里到底哪些记录落在允许域内。
所以前端即便把按钮放出来,最终后端还是会拦。
这也是为什么很多权限问题一定要在 ORM 层排查,不能只看界面。
四、sudo() 到底绕过了什么
源码里 _check_access() 一开头就有一个重要前提:
if not self.env.su ...
也就是说,当环境是 superuser 模式时,常规访问校验会被跳过。
这也是大家对 sudo() 又爱又怕的原因:
sudo() 的确能绕过权限检查
它可以绕过:
- 模型访问权限;
- 记录规则。
但 sudo() 不是“业务正确性”开关
它不会自动解决:
- 多公司一致性;
- 字段约束;
- 业务状态机限制;
- 你把不该写的数据写进去了以后留下的后遗症。
所以实战里最危险的不是不会用 sudo(),而是:
一遇到 AccessError 就立刻全链路
sudo()。
短期能过,长期往往把权限边界直接打穿。
五、最常见的三种误判
1)“表单能打开,所以写权限没问题”
错。打开表单通常只说明你有 read。
2)“我已经给了一个组写权限,为什么还报错”
错在只看 ACL,没看记录规则是不是把那条记录排除了。
3)“加个 sudo 就算修好了”
很多时候这不是修复,是绕过。
如果业务本意就是“普通用户只能改自己的记录”,那用 sudo() 把别人的也改了,等于把系统设计推翻了。
六、一个更实用的排查顺序
如果你遇到“能读不能写”,我建议按这个顺序查:
- 模型 ACL:这个用户所属组到底有没有
write; - 记录规则 domain:
write的规则和read的规则是不是不同; - 当前记录是否命中 write domain;
- 是否在多公司上下文下切错了 allowed companies;
- 是不是某段自定义代码提前
sudo()或反而丢了上下文。
这个顺序比一上来改 XML 按钮、改前端显隐、或者直接全局 sudo() 靠谱得多。
七、什么时候该用 sudo(),什么时候不该
适合用 sudo() 的场景
- 系统后台自动任务;
- 确实由系统代办、且业务上应越过当前用户边界的动作;
- 技术性同步、落日志、内部桥接。
不适合直接 sudo() 的场景
- 用户手动编辑业务单据;
- 你只是懒得梳理记录规则;
- 你并不确定越权后果。
更稳的做法通常是:
- 先把规则设计清楚;
- 必要时只在局部对象、局部步骤上
sudo(); - 并明确为什么这里允许系统越权。
总结
Odoo 的“能看不能改”不是怪现象,而是权限模型的正常表现。
从源码看,关键逻辑非常清楚:
- 先看 ACL,判断这个模型操作是否允许;
- 再看记录规则,判断这批记录是否允许当前操作;
read、write、unlink天生就是不同问题;sudo()能绕过检查,但不等于业务上就应该绕过。
如果只记一句,就记这句:
在 Odoo 里,看得见的是“读权限结果”,改得动才是“写权限结果”。两者从来不是同一件事。
DISCUSSION
评论区