noupdate 与升级

Odoo 改了 XML 数据为什么升级后没变:noupdate、ir.model.data 与模块升级边界讲透

很多 Odoo 开发都会遇到同一个困惑:明明改了 XML 里的记录,升级模块后数据库却不更新。问题往往不在“升级没跑”,而在 noupdate 和 ir.model.data 的升级语义。

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

先说结论

如果你在 Odoo 里改了一条 XML 数据,升级模块后数据库没变,最常见的根因不是:

  • 模块没升级
  • 文件没读到
  • 服务器没重启

而是:

这条记录背后的 XMLID 被 ir.model.data.noupdate 保护住了。

也就是说,Odoo 不是没认出这条记录,而是认出来了,但在 upgrade 模式下故意不改它


这件事为什么会让人反直觉

很多开发者对 XML 数据的直觉是:

  • XML 里这条记录长这样
  • 模块升级时再读一次
  • 数据库里这条记录就会同步成新样子

但 Odoo 的真实模型不是“文件覆盖数据库”,而是:

  • XML 记录通过 XMLID 映射到 ir.model.data
  • 升级时根据 XMLID 找到原记录
  • 再决定这次允不允许更新

所以真正的问题不是“找不找得到”,而是:

找到以后,当前升级语义是否允许覆盖。

而这个“是否允许”,noupdate 正是核心开关。


官方源码是怎么体现这个规则的

/home/ubuntu/odoo-temp/odoo/addons/base/models/ir_model.py 里,ir.model.data_update_xmlids()_build_update_xmlids_query() 很关键。

源码里最终构造的是一条 INSERT ... ON CONFLICT (module, name) DO UPDATE ... 查询。

最值得注意的一行是:

  • update=True(也就是模块升级语义)时
  • SQL 会加上 AND NOT ir_model_data.noupdate

这句话几乎可以直接翻译成人话:

如果这条 XMLID 被标记为 noupdate,那么 upgrade 时就算撞上同一个 XMLID,也不要更新它。

这就是为什么很多人改了 XML、升了模块、结果数据库没变。

不是失效,是规则本来就如此。


noupdate 真正在保护什么

它保护的不是 XML 文件本身,而是:

  • 这条 XMLID 对应的数据库记录
  • 在后续模块升级时,不被模块文件再次覆盖

你可以把它理解成一种“初始化后转为管理员接管”的语义。

最典型的保护对象包括:

  • 基础配置
  • 演示数据
  • 某些系统初始化记录
  • 安装后允许人工调整、以后不想再被升级改回去的记录

所以 noupdate 不是“永不变化”,而是:

不再由模块升级自动改。

这点非常重要。

因为管理员依然可以手工改,代码也依然可以显式改。只是模块升级时,默认不替你覆盖回去。


为什么这套机制是必要的

如果没有 noupdate,很多系统会很难用。

举个典型例子:

  • 你安装模块时导入了一套默认邮件模板
  • 后来管理员按公司口径改了文案
  • 你升级模块时,系统又把模板内容全覆盖回源码版本

这通常不是你想要的。

所以 Odoo 必须区分两种记录:

  1. 模块升级时应该继续被源码驱动的
  2. 初始化后应该更像“用户资产”的

noupdate 就是在做这个边界切分。


为什么 XMLID 和 noupdate 总是要一起理解

如果只理解 XMLID,你会知道:

  • Odoo 能稳定找到一条记录

但你还是解释不了:

  • 为什么有的记录升级会变
  • 有的记录升级不变

而如果再加上 ir.model.data.noupdate,整个故事才完整:

  • XMLID 负责“找到谁”
  • noupdate 负责“升级时能不能动它”

所以这两个概念其实是一套升级语义里的前后半句。


最常见的 4 个误判

1. 以为 XML 变了,数据库就一定会同步变

不一定。还要看这条 XMLID 的 noupdate

2. 以为 noupdate 表示记录以后不能改

不是。它只是不让模块升级自动覆盖。

3. 以为删掉数据库记录再升级就一定能恢复

这要看 XMLID、依赖、加载顺序和具体数据文件逻辑,不是简单一删就稳。

4. 以为“升级没生效”都是缓存问题

很多时候根本不是缓存,而是升级语义本来不允许更新。


什么时候你应该主动检查 ir.model.data

只要你遇到下面这些问题,就该第一时间怀疑这里:

  • 改了 XML 里的 menu / action / group / template / parameter,升级不生效
  • 某条基础数据明明有 XMLID,但更新总像被“吃掉”
  • 同一个 XMLID 在新库有效,在老库升级却没变
  • 你怀疑系统是在保留管理员曾经改过的结果

这时候排查思路应该是:

  1. 先确认 XMLID 是否一致
  2. 再看对应 ir.model.data 是否存在
  3. 再看 noupdate 是否为真
  4. 再判断当前是 install 还是 upgrade 语义

这个顺序通常比“先怀疑服务没重启”有用得多。


install 和 upgrade 的语义为什么不同

前面看 _build_update_xmlids_query() 时还有一个关键点:

  • 只有 update=True 时,才会额外加 AND NOT ir_model_data.noupdate

也就是说,noupdate 的阻断效果,是特别针对升级更新语义体现出来的。

这就意味着:

安装期和升级期,Odoo 对同一条 XMLID 的态度并不完全一样。

这也是为什么很多新库初装效果正常,老库升级却不跟着变。

问题常常不是“源码不同”,而是生命周期阶段不同


实战上怎么更稳地设计数据文件

一个很实用的原则是:

应该持续由模块控制的记录

别轻易放进会被 noupdate 保护的区域。

安装后允许管理员接管的记录

才考虑 noupdate 语义。

比如:

  • 某些系统行为规则、技术性动作、结构性配置,升级最好还能改
  • 某些邮件模板、展示文案、客户可读文本,安装后可能更适合交给业务方接手

这不是“技术对不对”的问题,而是谁拥有后续控制权的问题。


和现有文章怎么区分

站里已有一篇在讲 XMLID 与 env.ref 的稳定引用价值。

而本文的切入点不是“如何稳定找到记录”,而是:

  • 为什么 XML 数据升级时有时不更新
  • ir.model.datanoupdate 怎样参与升级 SQL 条件
  • install / upgrade 语义差异会怎样影响结果

也就是从模块升级的数据控制权去讲,而不是从“引用查找”去讲。


最后一句话

很多 Odoo 升级排错卡住,本质上不是你不会升级,而是你还在用“文件覆盖数据库”的直觉理解 XML 数据。

而官方源码真正表达的是:

升级时,Odoo 会先通过 XMLID 找到记录,再根据 noupdate 判断你有没有资格改它。

把这句话想明白,很多“为什么就是不生效”的谜团会立刻清楚很多。

DISCUSSION

评论区

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