关系命令

Odoo 的 One2many / Many2many 命令包:为什么一串三元组能同时新增、更新、解绑

讲清 Command.create / update / delete / unlink / link / clear / set 的语义,以及它们在 create、write、复制和批量改关系时的真实作用。

Odoo 开发 框架
进阶 开发者 1 分钟阅读
0 评论 0 点赞 0 收藏 5 阅读

先说结论

Odoo 里的 One2many / Many2many 并不是“字段值是一个列表”这么简单。

它们真正接受的是一套关系命令协议

  • 新增关联
  • 更新关联记录
  • 从关系里解绑
  • 直接删除关联记录
  • 清空整组关系
  • 用一批新 id 整体替换旧关系

这套协议在源码里被 odoo/orm/commands.pyCommand 类统一包装。你在 Python 里看到的是可读的 helper,底层落到数据库前,依旧是三元组。

一句话理解:

x2many 不是“写一个值”,而是“描述一次关系变更”。


七种命令分别在说什么

Command 把七种动作固定成常量:

  • create(0, 0, values):创建新记录并建立关系
  • update(1, id, values):更新已有关联记录
  • delete(2, id, 0):删除记录本身,再处理关系
  • unlink(3, id, 0):只断开关系
  • link(4, id, 0):把已有记录挂上来
  • clear(5, 0, 0):清空所有关系
  • set(6, 0, ids):用新 id 集合整体替换旧关系

其中最容易混淆的是 deleteunlink

  • delete 想删的是“数据库里的那条记录”
  • unlink 想删的是“和当前记录之间的关系”

对 Many2many 来说,这两者差得尤其大。一个是删行,一个是删关联。


One2many 和 Many2many 的 create 语义不一样

Command.create(values) 看起来很简单,但在两种关系里含义不同:

  • Many2many:所有当前记录共享创建出来的那一条 comodel 记录
  • One2many:每个父记录都会各自创建自己的子记录

这也是为什么同样写一个 Command.create(...),在 O2M 和 M2M 上的结果会不同。

如果你把 x2many 当成“数组 append”,就很容易误判最终数据结构。


为什么官方代码里还会出现 (4, id)Command.set(ids)

在旧代码和某些底层拼值场景里,你会看到:

'move_dest_ids': [(4, x) for x in self.move_dest_ids.ids]

这就是最原始的 link 命令。

而新代码更推荐:

'reference_ids': [Command.set(self.order_id.reference_ids.ids)]

理由很简单:

  • Command 更可读
  • 不容易把命令号写错
  • 代码审查时更容易看懂“你到底想做什么”

set() 的语义也很明确:先移除旧的,再补上新的。它不是“追加”,而是“整体替换”。


最常见的三个坑

不会。它只会断开关系。目标记录可能还被别的地方引用着。

2. 想保留旧关系,却用了 set([...])

set 会把不在新列表里的旧关系全部清掉。很多“关系突然少了一半”的问题都来自这里。

3. 在循环里一条条 write 关系字段

如果你已经知道最终目标关系是什么,通常直接构造命令列表一次性写入更稳,也更省 SQL。


读源码时要抓住的一个现实

Command 只是 Python 层的“好名字”。

真正重要的是:ORM 在把命令翻译成关系变化时,会根据字段类型、约束、权限和联动逻辑做不同处理

这也解释了为什么同样是“加一条关联”,有时候会触发创建子记录,有时候只会多一个中间表行,有时候还会连带影响复制、缓存或 recompute。


实战建议

如果你在写自定义模块,可以按这个顺序判断:

  1. 你想改的是“记录本身”还是“和当前对象的关系”
  2. 如果是关系,先想清楚是新增、替换、解绑还是删除
  3. 能用 Command.* 就尽量不用裸三元组
  4. 遇到异常时,先看最终命令列表,再看 ORM 是否把它转成了你预期的动作

只要把这层协议想明白,x2many 就不会再像魔法。


结尾一句话

One2many / Many2many 的本质不是列表,而是关系变更指令集

看懂 create / update / delete / unlink / link / clear / set,你就看懂了大半个 Odoo 关系字段写入协议。

DISCUSSION

评论区

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