Odoo 开发

Odoo 为什么新增 search_fetch:它不是 search_read 的语法糖,而是 ORM 取数分层

很多人看到 search_fetch 会把它理解成 search_read 的另一个名字,但官方源码里它更像 search 与 fetch 的拼接层:先拿到满足 domain 的 query,再把指定字段灌进缓存。本文讲清它和 search、read、search_read 的分工边界。

Odoo 开发
进阶 开发者 1 分钟阅读
0 评论 0 点赞 0 收藏 7 阅读

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() 的职责很单纯:

  • 根据 domain、offset、limit、order
  • 找到满足条件的 id
  • 返回 recordset

但这个 recordset 刚拿到手时,并不代表你要用的字段都已经准备好了。你后面一旦在循环里访问:

  • partner.name
  • partner.company_id
  • partner.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()
  • 只想拿 idssearch() 足够
  • 根本不确定后面会不会读这些字段:提前 fetch 可能只是白做工

五、一个够实用的判断法

如果你更关心:

  • 输出结果给接口 → 用 search_read()
  • 保留 recordset 并优化后续字段访问 → 用 search_fetch()

结语

search_fetch() 的价值,不是又多了一个“查数据方法”,而是把 搜索结果的 recordset 语义字段预取的性能需求 接了起来。

它和 search_read() 最大的不同,不在名字相似,而在思路完全不同:

  • search_read() 关心“我要把结果吐出去”
  • search_fetch() 关心“我要把结果留在 ORM 里,但别让我后面再碎着查”

把这个分层理解清楚,你读 Odoo 新版源码时,会更容易看懂为什么框架内部越来越常用它。

DISCUSSION

评论区

想参与讨论?先 登录 再发表评论。
还没有评论,你可以成为第一个留言的人。