视图解析顺序

Odoo 视图为什么明明都继承同一处却结果不一样:解析顺序、priority 和 primary mode 讲透

很多人知道 Odoo 有 XPath 继承,却不清楚多个扩展视图一起叠上来时,系统到底按什么顺序组合。本文从 ir_ui_view.py 源码讲清 priority、primary mode、深度优先遍历与常见误判。

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

先说结论

很多人把 Odoo 视图继承理解成:

  • 找到基视图
  • 写一个 XPath
  • 系统把改动贴上去

这只说对了一半。

真正麻烦的地方在于:一个页面往往不是只有你这一层继承。同一个 form/tree/search 视图,可能同时被多个模块、多个自定义模块、多个 Studio 扩展一起修改。

所以你真正要问的,不是“我的 XPath 对不对”,而是:

当很多继承视图同时存在时,Odoo 到底按什么顺序把它们组合成最终视图?

这件事在 base/models/ir_ui_view.py 里写得很清楚,核心在这几个点:

  • priority 决定同层扩展的大体先后
  • mode 决定一个视图是“扩展层”还是“新的主根”
  • _combine() 用的是带特殊规则的深度优先遍历
  • primary 视图不会像普通 extension 一样立刻插进去,而是会在当前主树的 extension 处理完后再继续展开

理解了这几件事,你调试“为什么 XPath 有时生效、有时被别人盖掉”就会快很多。


先把两个角色分清:extension 和 primary

在 Odoo 里,不是所有带 inherit_id 的视图都扮演同一种角色。

extension:在父视图上继续打补丁

这是最常见的情况。

你继承某个 view,然后写 XPath 去插字段、改属性、替换节点。

这种视图的作用是:

在已有主视图上继续叠加修改。

它不是新的根,只是修改链中的一个补丁层。

primary:成为一棵新的“主视图根”

primary 更像是:

我承认自己来源于某个父视图,但我希望最终对外被当作一棵新的主视图来继续挂子扩展。

这也是为什么某些继承链里,你会看到“同样 inherit 某个父视图”,但最终组合顺序明显和普通扩展不一样。

因为它不是简单补一刀,而是把自己提升成后续组合的主干节点。


源码里真正的关键:_combine()

ir_ui_view.py 里,_combine() 的注释几乎直接把设计意图写出来了。

它要做的是:

  • 先从当前 primary 视图的 arch 出发
  • 再把整棵继承树按顺序遍历
  • 每走到一个子视图,就调用 apply_inheritance_specs() 把修改应用到当前组合结果上

源码里最值得注意的一句不是 XPath 本身,而是这句思路:

对当前 primary view,先遍历它的各种扩展;如果途中遇到子 primary,则把那个 primary 放到队列另一端,等当前分支的 extension 处理完后再进入它。

这意味着:

  • extension 更像“立刻叠加”
  • primary 更像“下一层主树入口”

所以最终顺序不是“看到谁先处理谁”,而是一个带优先规则的深度优先过程。


为什么官方说是“特殊的深度优先遍历”

源码里用 collections.deque 来维护遍历队列。

规则大意是:

  • 普通 extension 子视图:往左边压,尽快处理
  • primary 子视图:往右边放,延后处理

这个设计非常像:

  1. 先把当前主树应该叠上的补丁都叠完
  2. 再进入下一棵从它分叉出去的主树

如果你把它想成“分支代码合并”会比较容易理解:

  • extension 是在当前分支继续 commit
  • primary 是从某个时点切出去,形成后面还会继续长孩子的新主分支

这也解释了为什么某些视图问题,光看 inherit_id 还不够,你还得看它是不是 primary,以及它在树里的层级位置


priority 真正影响什么

同一个父视图下面往往会挂很多子视图。

源码在构造队列时会先做 sorted(hierarchy[self], key=lambda v: v.mode),而实际子视图集合本身又是按数据库查询顺序和 priority 参与组合。你在业务上真正感受到的是:

  • priority 小的,通常更早参与组合
  • priority 大的,通常更晚叠上去

而“更晚叠上去”意味着什么?

1. 后来的 attributes 可能覆盖前面的属性修改

比如前一个扩展把字段设成 readonly="1",后一个又改回动态表达式,那么最终你看到的是后者。

2. 前面的结构改动会影响后面 XPath 能不能找到节点

这是最常见的坑。

你以为自己的 XPath 没写错,实际上是:

  • 前一个扩展把节点搬走了
  • 或者已经 replace
  • 或者外层结构被重组了

于是你的 XPath 匹配路径在“最终待处理的中间 arch”里已经不存在。

3. 不是所有“后执行”都等于“更强”

有些人会把 priority 理解成 CSS 权重,这是不对的。

因为后面的视图必须先找得到目标节点,才有资格覆盖前面。

如果节点都被改没了,priority 再大也没用。


为什么“我继承的是同一个父视图”,结果却和别人的扩展相互影响

因为 Odoo 不是给每个扩展一份独立父视图副本。

它是把所有扩展叠到同一条组合链里。

也就是说:

  • 你的 XPath 面向的是“当前已组合到这一步的中间结果”
  • 不是最初那份官方原始 XML

这点非常关键。

很多人调试失败,是因为脑子里看的还是原模块 XML;但程序实际操作的,已经是:

原始视图 + 若干前置继承之后的组合 arch

所以在复杂项目里,调视图的正确方式不是只看基视图,而是看:

  1. 基视图是谁
  2. 当前链上还有哪些继承视图
  3. 它们的 priority 怎么排
  4. 哪些是 extension,哪些是 primary
  5. 你自己的 XPath 是打在“原图”上,还是打在“别人改过的图”上

apply_inheritance_specs() 为什么是调试核心

_combine() 每处理一个子视图,最终都落到 apply_inheritance_specs()

这个方法本身不神秘,它做的就是:

  • 定位 spec 节点
  • 根据 position 执行 after / before / inside / replace / attributes / move 等修改
  • 如果定位失败,抛出视图错误

所以很多视图问题本质上不是“前端没刷新”,而是:

  • 组合顺序让你的目标节点已经变形
  • 于是 spec 应用点变化了
  • 或者直接定位失败

当你把问题归因从“XPath 写错”升级到“继承树顺序导致 XPath 对象变化”,思路会立刻清晰很多。


最容易误解的 4 个点

误解 1:inherit_id 一样,作用面就一样

不对。

即使两个视图都继承 sale.view_order_form,只要:

  • priority 不同
  • 中间还夹着别的 extension
  • 或者其中一个是 primary

它们对最终视图的影响位置就可能完全不同。

误解 2:priority 只是在“冲突时”才重要

不对。

priority 不只是冲突解决,它直接决定了:

  • 谁先改结构
  • 谁后面基于谁来匹配 XPath

换句话说,它影响的不是最终一行配置,而是整个继承链的中间态

误解 3:换个更长的 XPath 就能解决一切

也不对。

更长的 XPath 只是让定位更具体,不会改变组合顺序。如果前置扩展把你依赖的层级结构整体换掉,长 XPath 反而更脆。

误解 4:primary 只是少见配置,可以忽略

复杂系统里不能忽略。

特别是多套后端界面、不同业务入口或平台扩展共存时,primary 会改变视图树组织方式。你忽略它,就很容易看错“谁才是当前主根”。


实战里怎么避免把自己写进死胡同

1. 尽量锚定稳定节点

优先找:

  • 语义稳定的字段
  • 官方长期不太会改名的容器
  • 而不是第几个 group、第几个 page

因为前者更能穿越前置扩展带来的结构漂移。

2. 能做属性增量,就别轻易 replace 大块节点

replace 很爽,但破坏面最大。

你一旦整块替掉,后面别人原本打在旧节点上的 XPath 很可能一起失效。

3. 改优先级前先判断自己是在“覆盖结果”还是“依赖前置结构”

如果你只是想最后改个属性,提高 priority 可能有效。

但如果你依赖另一个扩展先把节点插进来,priority 提太高反而会让你先执行,结果找不到节点。

4. 调试时看“整棵链”,不要只看一份 XML

复杂项目里,视图问题几乎从来不是单文件问题。

你需要把它当成:

一个由多个模块共同维护的组合树

而不是“我这一段 XML 为什么没生效”。


一句话记住

如果只记一条,就记这句:

Odoo 视图继承不是把每个扩展分别贴到原视图上,而是沿着继承树按特定顺序不断修改同一份组合结果。

所以:

  • priority 决定谁先参与
  • primary 决定谁是后续主树
  • 前置扩展会改变后置 XPath 的操作对象

这就是为什么在 Odoo 里,视图问题经常不是“单点错误”,而是“组合顺序错误”。

你一旦用这个视角看问题,很多诡异现象都会突然合理起来。

DISCUSSION

评论区

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