先说结论
很多开发者第一次接触 fields_get(),会把它理解成:
- “把字段列表吐出来”
- “给导入导出或 API 看一下字段”
但它在 Odoo 里的真实角色更接近:
模型对外公开“字段元数据”的统一入口。
它返回的不只是字段名,还包括:
- string
- help
- type
- readonly
- required
- selection
- searchable
- 以及其他描述属性
更重要的是,这些返回值并不是永远固定的。
官方源码里的 fields_get() 在做什么
在 /home/ubuntu/odoo-temp/odoo/orm/models.py 里,fields_get() 的逻辑非常清楚:
- 遍历
self._fields - 按
allfields过滤目标字段 - 先检查当前用户是否有字段级读取权限
- 再调用
field.get_description(self.env, attributes=attributes)生成描述 - 如果 description 里有
readonly,还会结合写权限进一步修正
最后返回的是:
- 一个以字段名为 key 的字典
- value 是该字段的描述字典
这里最值得注意的是三点。
1. 它会做字段级权限过滤
所以 fields_get() 不是“模型有啥就全吐啥”,而是:
当前 env 允许你看到什么字段元数据,才给你什么。
2. 它的 string / help / selection 会走翻译语境
源码注释直接写了:
_inherits带来的字段也会包含进来string、help、selection等描述会按当前语言翻译
3. readonly 不是机械照抄 field 定义
如果用户对这个字段没有写权限,返回描述里可能会把它视作只读。
这说明:
字段元数据不是纯静态 schema,而是“当前语境下的可用描述”。
为什么同一个字段标签会在不同地方看起来不一样
如果你只把字段理解成“数据库列定义”,你会很困惑:
- 为什么同一个字段在不同界面标题不一样?
- 为什么有时叫 Forecasted Quantity,有时又不是?
- 为什么某些自定义字段在某个用户下像不存在?
答案之一就是:fields_get() 可以被模型重写,并且经常会结合上下文动态改描述。
官方源码里就有很好的例子。
stock.product 的上下文改名
在 /home/ubuntu/odoo-temp/addons/stock/models/product.py 中,fields_get() 会读取:
locationsearch_location
然后根据库位 usage 动态改字段标签。
比如:
- supplier 库位下,
qty_available会显示成Received Qty - internal 库位下,
virtual_available会显示成Forecasted Quantity - customer / inventory / production 下又会有不同文案
这非常能说明问题:
同一个字段,字段本体没变,但它面对当前业务语境时,描述可以变。
为什么 fields_get() 不是单纯给前端看的
它当然会被前端、导入导出、API、视图后处理等地方使用。
但更本质地说,它解决的是:
系统怎样把“模型内部字段定义”转换成“当前用户当前语境下可消费的字段说明”。
这也是为什么很多框架逻辑都要依赖它,而不是直接去扒 _fields。
因为 _fields 更接近模型定义本身;
而 fields_get() 更接近“解释给外部系统听”的那一层。
res.users 的例子为什么更能说明权限语义
在 /home/ubuntu/odoo-temp/odoo/addons/base/models/res_users.py 里,fields_get() 还做了一个很典型的补丁:
- 先拿父类结果
- 再把“用户自己可读/可写”的字段补回来
- 对补回来的字段动态标记
readonly和searchable=False
这说明 fields_get() 不只是“把 field description 原样往外倒”,而是能表达:
- 哪些字段当前可见
- 哪些字段当前可写
- 哪些字段虽可读但不适合作为普通搜索元数据暴露
也就是说,它是一个权限 + 元数据解释层。
开发里最常见的误解
误解 1:fields_get() 等于数据库字段定义
不是。
它更像“当前语境下的字段说明书”。
误解 2:字段标签改了,就等于字段本体改了
很多时候你只是改了 fields_get() 返回的 string,不是改字段定义本身。
误解 3:直接读 _fields 就够了
如果你只关心模型类定义,读 _fields 可以。
但如果你关心:
- 当前用户能看到什么
- 字段在当前语言里叫什么
- 当前上下文下标签怎么变
就不能跳过 fields_get()。
实战里什么时候该考虑重写 fields_get()
比较适合的场景有:
- 字段标签确实需要随上下文变化
- 某些字段元数据需要按权限做额外包装
- 你要给前端或外部调用返回更贴业务语义的字段描述
但也别滥用。
如果只是想改固定标签,优先:
- 字段定义
- 翻译
- 视图层
因为一旦把太多展示逻辑塞进 fields_get(),你就会让字段元数据变成一个隐藏副作用层,调试成本会上升。
一句话收尾
fields_get()不是“字段列表接口”,而是 Odoo 把字段定义翻译成“当前用户、当前语言、当前上下文可消费元数据”的解释层。
DISCUSSION
评论区