先说结论
Odoo 的视图继承,不是“在 XML 上随便加几个 xpath”。
它其实是一个完整的组装流程:
- 先找到基础视图
- 再把所有扩展视图按顺序叠上去
- 每个 xpath 都会被验证
- 有问题的定位器会被标出来
- 最终拼成一个组合后的 arch
所以,视图继承的本质不是“改一小段 XML”,而是 把多个视图规格合成最终页面。
inherit_id 和 mode 决定了继承方式
在 ir.ui.view 里,两个字段最重要:
inherit_idmode
源码注释已经把行为说得很清楚:
extension 模式
如果这个视图是 extension:
- Odoo 先找到最近的 primary view
- 再把同模型下的扩展视图叠加上去
primary 模式
如果这个视图是 primary:
- Odoo 先把底层 primary 视图完全解析出来
- 再把当前视图自己的 inheritance specs 叠上去
这意味着 mode 不只是一个标签,而是决定了“先谁后谁”的拼装方式。
为什么 XPath 校验这么严格
在 _valid_inheritance() 里,Odoo 会检查继承表达式。
它有两个很实用的约束:
- 不能拿被翻译的属性当选择器
- class 选择不要直接写死,最好用
hasclass()
这个设计的原因很简单:
- 翻译会让文本类选择器不稳定
- 直接写
@class='...'很脆弱 - 视图继承需要可维护,而不是“今天能跑,明天就坏”
所以 Odoo 更推荐稳定定位,而不是依赖易变文本。
_compute_invalid_locators() 会帮你找错位点
这是一个非常贴心的调试机制。
当继承视图有问题时,Odoo 不只是报一个“XML 错了”,而是会尝试找出:
- 哪个 locator 找不到目标
- 哪个 xpath 语法有问题
- 哪个子 spec 失效了
它会先把上层视图组合起来,再逐个分析当前视图里的 specs。
这说明一个很重要的事实:
后面的 xpath 可能依赖前面的 xpath。
所以 Odoo 是按顺序应用 spec 的,不是一次性静态判断完就结束。
apply_inheritance_specs() 才是“真正改页面”的地方
当一个视图通过校验后,真正的合成动作由 apply_inheritance_specs() 完成。
它会根据 spec 的 position 执行不同操作,比如:
beforeafterinsidereplaceattributesmove
你可以把它理解成:
xpath 负责“找位置”,position 负责“怎么改”,apply_inheritance_specs 负责“真正落地”。
这三者缺一不可。
新手最容易踩的 4 个坑
1)拿中文文案做定位
页面文本会变,翻译也会变,太脆弱。
2)盯着某个具体节点顺序不放
父视图一升级,节点位置可能就变了。
3)在 class 上直接写死匹配
源码已经提醒你:优先用 hasclass()。
4)以为一个 xpath 写错只是“局部坏掉”
在 Odoo 里,继承校验失败可能会影响整张组合视图。
实战建议
如果你在改视图,建议按这个思路:
- 先找稳定锚点,尽量用 id、name、数据字段或结构节点
- 再决定
position是改属性还是替换节点 - 复杂页面尽量拆成多个小继承,而不是一个巨大的 xpath
- 如果报错,先看 invalid locator,再看父视图是否已经变化
视图继承看起来像 XML 技巧,其实本质上是 页面结构的源码治理。
理解了这点,你写出来的继承就会稳很多。
DISCUSSION
评论区