Odoo 里和“查数据”有关的方法很多:search()、read()、search_read()、name_search(),近版本里又越来越常看到 search_fetch()。很多人第一反应是:
哦,这大概就是
search_read()的新写法。
这其实不对。
从官方源码看,search_fetch() 的定位不是“直接给前端返回字典”,而是:
- 先完成搜索
- 再把字段放入 ORM cache
- 返回的仍然是 recordset
所以它真正解决的是 ORM 内部如何以尽量少的 SQL,把后续可能会访问的字段预先准备好,而不是替代 search_read() 的接口输出职责。
一、源码先说结论:search_fetch = search + fetch
在 /home/ubuntu/odoo-temp/odoo/orm/models.py 里,search_fetch() 的文档字符串已经写得很明确:
- 它先通过
_search()生成 query - 再调用
_determine_fields_to_fetch()计算要预取哪些字段 - 最后走
_fetch_query()把字段塞进缓存
注意关键词:fetch to the cache。
这说明 search_fetch() 的重点不是把结果序列化出来,而是把数据先放进 Odoo ORM 自己的读取缓存里,让后续 Python 层访问字段时少打 SQL。
也正因此,它最终返回的仍然是 recordset,而不是字典列表。
二、它和 search() 差在哪
search() 的职责很单纯:
- 根据 domain、offset、limit、order
- 找到满足条件的 id
- 返回 recordset
但这个 recordset 刚拿到手时,并不代表你要用的字段都已经准备好了。你后面一旦在循环里访问:
partner.namepartner.company_idpartner.country_id.name
ORM 才会继续触发取数、预取、关联访问。
而 search_fetch() 的差别在于:
- 它不仅把记录找出来
- 还会顺手把指定字段放进 cache
所以如果你明确知道后面一定会读某几个字段,search_fetch() 往往更贴近“搜索 + 预热缓存”的真实需求。
三、它和 search_read() 的根本区别,不在 SQL,而在返回语义
很多人把两者混着用,是因为它们都像在做“搜然后读”。但职责完全不同。
search_read() 更像接口输出方法
它通常用于:
- RPC
- 控制器接口
- 给前端/外部返回 JSON 风格字典
调用后拿到的是:
[
{'id': 1, 'name': 'A'},
{'id': 2, 'name': 'B'},
]
search_fetch() 更像 ORM 内部优化方法
它拿到的仍是 recordset:
records = self.search_fetch(domain, ['name', 'partner_id'])
随后你还可以继续走标准 ORM 风格:
records.filtered(...)records.mapped(...)records.write(...)records.partner_id
也就是说:
search_read()偏“结果输出”search_fetch()偏“结果保留为 ORM 对象,但提前把字段装进缓存”
这是两者最根本的分工边界。
四、search_fetch() 真正值钱的地方:减少“先搜后读”的碎 SQL
源码里的 _fetch_query() 很值得看。它会区分:
- 可以直接从列读取的字段
- 需要单独
read()逻辑的非列字段
对列字段,它会构造 SQL 一次性取出,然后把值插入 cache;对其他字段,再调用字段自己的 read() 逻辑。
这意味着它不是简单粗暴地“帮你多 read 一次”,而是在尽量沿 ORM 现有机制,用 最少 SQL + 正常缓存填充 的方式做预取。
什么时候它最合适
- 后面马上会遍历 recordset 并访问多个字段
- 你还想保留 ORM 的 recordset 语义
- 你在写模型层、服务层或框架层逻辑
- 你正在排查 search 后字段访问太碎的性能问题
什么时候别硬上
- 最终就是要返回字典给前端:直接
search_read() - 只想拿
ids:search()足够 - 根本不确定后面会不会读这些字段:提前 fetch 可能只是白做工
五、一个够实用的判断法
如果你更关心:
- 输出结果给接口 → 用
search_read() - 保留 recordset 并优化后续字段访问 → 用
search_fetch()
结语
search_fetch() 的价值,不是又多了一个“查数据方法”,而是把 搜索结果的 recordset 语义 和 字段预取的性能需求 接了起来。
它和 search_read() 最大的不同,不在名字相似,而在思路完全不同:
search_read()关心“我要把结果吐出去”search_fetch()关心“我要把结果留在 ORM 里,但别让我后面再碎着查”
把这个分层理解清楚,你读 Odoo 新版源码时,会更容易看懂为什么框架内部越来越常用它。
DISCUSSION
评论区