先把最容易说错的一点摆出来
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_idview_typeoptions.mobileself.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 要在之后移除
这意味着你最终看到的页面并不是“缓存里原封不动那份”。
更准确地说,是:
- 先命中某个视图缓存变体
- 再按当前用户组把不该看到的块裁掉
所以两个用户即使命中同一份基础缓存,最后看到的结果也可能不同。
这就是为什么:
- 管理员看到字段
- 普通用户看不到
不一定是加载到了不同 arch,也可能是同一份缓存结果经过了不同的 group 裁剪。
第五层:get_view() / get_views() 对“可依赖的上下文”是有限制的
源码文档反复强调:
返回结果只能依赖这些东西:
- 请求的 view type
- 访问权限 / view access rules
- options
langTYPE_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 个问题:
- 这次请求命中的
view_id / view_type / lang / mobile是什么 - 有没有
*_view_ref在改入口 - 当前模型是否重写过
_get_view_cache_key() - 当前
_get_view()是否依赖了额外上下文却没进 key - 结果差异是缓存变体不同,还是同一变体被 group 裁剪成了不同页面
这样排,比直接盯着 XPath 有用得多。
最后一句话
Odoo 视图缓存真正缓存的,不是“某个 XML 文件”,而是:
某个上下文维度下、继承合成并后处理后的视图变体。
所以你想解释“为什么明明改了却像没生效”,先看 cache key 和变体边界,往往比先怀疑 XPath 更接近真相。
DISCUSSION
评论区