很多人第一次接触 fields_get(),会觉得它只是个“拿模型字段定义”的方法:返回 string、type、required、selection 之类元数据,供前端画表单用。
这当然没错,但如果只把它看成“字段说明书”,你会漏掉它非常关键的一层:权限裁剪。
在官方源码里,fields_get() 并不是无条件把 _fields 全吐出去,而是会先判断当前用户有没有读这个字段的资格;即使字段能读,还会根据写权限把返回结果里的 readonly 调整掉。这意味着它不是纯描述接口,而是 描述 + 权限投影 的混合入口。
一、源码里 fields_get() 真正在做什么
在 /home/ubuntu/odoo-temp/odoo/orm/models.py 里,fields_get() 的主流程不复杂:
- 遍历模型的
_fields - 如果调用方指定
allfields,只保留目标字段 - 用
_has_field_access(field, 'read')判断能不能读 - 调
field.get_description()生成字段描述 - 如果描述里有
readonly,再结合写权限补一次判断
重点就在第 3 步和第 5 步。
也就是说,fields_get() 返回的不是“字段客观真相”,而是:
当前用户在当前环境下,被允许看到的字段描述。
二、为什么它会直接影响前端“看得见什么”
很多 Web 端视图加载、字段说明、动态 UI 构建,本质上都依赖字段元数据。如果某个字段在 fields_get() 里直接被过滤掉,那前端通常连“这个字段存在”都不会正常感知到。
所以字段级 groups 限制,不只是“写不了”,很多时候是:
- 看不到
- 拿不到描述
- 视图解释阶段就不会当成普通可读字段处理
这也是为什么某些权限问题看起来像“字段消失了”,而不只是“按钮变灰了”。
三、readonly 为什么不是只看字段定义本身
很多人以为 readonly=True 才会让前端只读,其实 fields_get() 里还有一道补充逻辑:
if 'readonly' in description:
description['readonly'] = description['readonly'] or not self._has_field_access(field, 'write')
这段逻辑非常关键。它表达的是:
- 字段本身定义成只读,那当然只读
- 即便字段定义上不是只读,只要当前用户没写权限,返回给前端时也会被视为只读
所以前端看到的 readonly,不完全等于模型声明,而是:
字段声明的只读性 + 当前用户的写权限结果
这也是为什么同一张表单,不同用户打开时,感受到的可编辑性会不同。
四、_has_field_access() 才是字段级权限的真正门卫
同一文件里的 _has_field_access() 规则很直接:
- 字段没配 groups,或者当前是 superuser → 允许
- 字段 groups 是
NO_ACCESS→ 直接拒绝 - 否则看当前用户是否属于这些 group
这个设计说明 Odoo 的字段级权限,不是单靠模型 ACL 或记录规则撑起来的。字段本身就能声明一层“谁能看/谁能改”的门槛。
也因此,开发里要区分三种边界:
- 模型能不能访问
- 记录能不能看到
- 字段能不能暴露
很多排错之所以绕,是因为大家只盯前两层,漏了第三层。
五、为什么 res.users 又看起来有点“例外”
在 /home/ubuntu/odoo-temp/odoo/addons/base/models/res_users.py 里,res.users 重写了 _has_field_access(),允许用户对“自己那一份”某些安全字段有额外读取权限。
这很有代表性。
它说明字段级权限并不是一个死板总开关,而是可以按模型语义扩展。例如用户档案里,系统可能希望:
- 普通用户不能读很多管理字段
- 但对自己的某些资料字段,仍然应该能看
所以你看到的最终字段可见性,是:
- 通用字段 groups 规则
- 模型定制的
_has_field_access() - 当前记录是否“就是自己”
共同作用的结果。
六、新手最容易误解的 3 件事
1)“fields_get 只是文档接口,不影响权限”
错。它直接就是权限投影层的一部分。
2)“字段只读一定是 XML 或 Python 定义出来的”
不完全对。用户没写权限时,fields_get() 也会把它表现成只读。
3)“字段消失一定是视图 XPath 写坏了”
不一定。很可能是字段级读权限把它从元数据里直接裁掉了。
七、实战排错时该怎么想
如果你遇到“某字段在界面上不见了 / 变只读了 / 某用户和管理员看到的不一样”,排查顺序最好是:
- 先确认模型 ACL 和记录规则
- 再看字段本身有没有
groups - 再确认模型是否重写了
_has_field_access() - 最后才去怀疑视图层 XPath、attrs 或 modifiers
这样通常会比一上来死查 XML 快很多。
结语
fields_get() 之所以重要,不是因为它能返回字段说明,而是因为它把 字段描述 和 字段级权限结果 合在了一起。
所以它从来不是“单纯元数据接口”,而是前端可见性、可编辑性和字段暴露范围的关键中间层。把这层想清楚,很多“为什么这个字段在某个用户眼里像不存在”之类的问题,都会瞬间好排查很多。
DISCUSSION
评论区