视图缓存

Odoo 视图为什么“明明改了却像没生效”:`_get_view_cache_key()`、变体缓存与最终裁剪

很多人一看到视图改动没显示,就立刻怀疑 XPath、怀疑浏览器缓存。但从 Odoo 19 的 `ir_ui_view.py` 看,真正需要先想清的是:这次拿到的是哪一个 cache key、这个 key 下缓存的是哪一种视图变体,以及用户最终看到的是否还是缓存结果再经过 group 裁剪后的版本。本文把这个链路讲透。

前端 框架
进阶 开发者 1 分钟阅读
0 评论 0 点赞 0 收藏 6 阅读

先把最容易说错的一点摆出来

Odoo 里“视图没更新”不一定等于:

  • XPath 没命中
  • 修改没保存
  • 浏览器缓存没清

很多时候更接近下面这个事实:

你这次请求拿到的,可能根本不是你脑子里想的那一种视图变体。

而决定“是不是同一种变体”的关键,就是 _get_view_cache_key()


第一层:Odoo 缓存的不是原始 XML,而是“可复用的视图结果”

ir_ui_view.py_get_view_cache() 上直接挂了 ormcache(..., cache='templates')

它缓存的不是简单的原始 arch 字符串,而是:

  • 通过 _get_view() 拿到继承合成后的 arch
  • 再经过 _get_view_postprocessed() 后处理
  • 最终返回包含 arch / id / model / models 的结果

也就是说,缓存的是一个已经很接近前端可消费结果的产物。

所以你看到页面没变,不一定是“重新算视图失败”,也可能只是还在命中旧的视图结果缓存


第二层:缓存键默认不只看 view_id

默认 _get_view_cache_key() 返回的东西包含:

  • view_id
  • view_type
  • options.mobile
  • self.env.lang
  • context 里所有 *_view_ref

这意味着同一张视图,天然就可能有多份缓存:

  • form 和 list 不是一份
  • mobile 和 desktop 不是一份
  • 中文和英文不是一份
  • 某些通过上下文指定 view ref 的场景也不是一份

所以“我明明在另一个入口看到更新了,这个入口还没变”并不神秘。

它可能压根就是不同 cache key。


第三层:有些模型会继续扩展 cache key

这点特别值得开发者记住。

比如 res_partner.py 里重写 _get_view_cache_key(),把:

  • 当前公司
  • no_address_format

也拼进 key。

因为 partner 地址视图会随公司地址格式变化。

res_currency.py 也类似,会把公司币种名拼进去,因为字段标签会跟公司币种相关。

这说明:

只要某个模型的 _get_view() 会根据额外上下文改 arch,它就应该把这些因素补进 cache key。

否则就会出现最经典的缓存污染:

  • A 场景算出来的视图
  • 被 B 场景错误复用

第四层:缓存结果里其实还保留了“所有组”的结构,再按用户裁剪

_get_view_cache() 的注释写得很关键:

  • cached view 包含了 inherited / postprocessed 结果
  • 并且是 for all groups
  • 对于不属于相关 group 的用户,受限 block 要在之后移除

这意味着你最终看到的页面并不是“缓存里原封不动那份”。

更准确地说,是:

  1. 先命中某个视图缓存变体
  2. 再按当前用户组把不该看到的块裁掉

所以两个用户即使命中同一份基础缓存,最后看到的结果也可能不同。

这就是为什么:

  • 管理员看到字段
  • 普通用户看不到

不一定是加载到了不同 arch,也可能是同一份缓存结果经过了不同的 group 裁剪


第五层:get_view() / get_views() 对“可依赖的上下文”是有限制的

源码文档反复强调:

返回结果只能依赖这些东西:

  • 请求的 view type
  • 访问权限 / view access rules
  • options
  • lang
  • TYPE_view_ref

它还明确说:别的 context 值不能拿来影响返回结果。

这其实是在提醒开发者:

如果你自定义视图逻辑时偷偷依赖一堆任意 context key,却没同步扩展 cache key,你迟早会撞上“这个人看到的是另一个场景的视图”这种脏缓存问题。


开发里最常见的 4 个误判

1. 把所有“没更新”都怪到浏览器缓存

浏览器当然可能缓存静态资源,但服务端视图本身也有自己的变体缓存逻辑。

2. 只检查 XPath,不检查 cache key 维度

如果不同语言、不同移动端、不同 _view_ref 本来就是不同 key,你只在一个入口验证,结论很容易错。

3. 在 _get_view() 里按自定义 context 改 arch,却不改 _get_view_cache_key()

这是最典型的自定义坑。

4. 看到不同用户结果不同,就以为缓存错乱

不一定。

有时只是同一份缓存经过 group 裁剪后,最终页面不同。


一套很实用的排查顺序

当你怀疑“视图改了却像没生效”时,我建议先问这 5 个问题:

  1. 这次请求命中的 view_id / view_type / lang / mobile 是什么
  2. 有没有 *_view_ref 在改入口
  3. 当前模型是否重写过 _get_view_cache_key()
  4. 当前 _get_view() 是否依赖了额外上下文却没进 key
  5. 结果差异是缓存变体不同,还是同一变体被 group 裁剪成了不同页面

这样排,比直接盯着 XPath 有用得多。


最后一句话

Odoo 视图缓存真正缓存的,不是“某个 XML 文件”,而是:

某个上下文维度下、继承合成并后处理后的视图变体。

所以你想解释“为什么明明改了却像没生效”,先看 cache key 和变体边界,往往比先怀疑 XPath 更接近真相。

DISCUSSION

评论区

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