伪关系字段

Odoo Reference 和 Many2oneReference 为什么看着像关系字段却没有外键:伪关系字段的真实边界

Odoo 里有一类字段看起来像能指向任意模型,但它们并不像 Many2one 那样由数据库外键保护。本文从官方源码讲清 Reference 与 Many2oneReference 的存储方式、适用场景与风险边界。

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

先说结论

ReferenceMany2oneReference 最容易让人误会的地方,是它们长得像“关系字段”,却不是数据库层面的普通外键关系。

官方源码在 odoo/orm/fields_reference.py 里直接把它们叫做:

Pseudo-relational field(伪关系字段)

这句话非常关键。

因为它意味着:

  • 它们确实表达“指向某条记录”
  • 但数据库不会像 Many2one 那样用标准 FK 帮你兜底

所以它们的价值是灵活,代价是边界更需要开发者自己懂。


为什么 Odoo 需要这种字段

因为有些场景,目标模型不是固定一个。

例如:

  • 菜单 action 可能指向 report、window action 等不同模型
  • 附件可能挂到任意业务对象
  • 某些评分、通用资源、通用引用对象,天然就跨模型

这类需求如果硬塞成普通 Many2one,你会立刻卡住:

  • Many2one 只能固定指向一个 comodel
  • 但你的目标记录可能来自多个模型

所以 Odoo 需要一种“可变目标模型”的引用机制。


Reference 是怎么存的

源码里写得很清楚:

  • Reference 在数据库里存的是一个字符串
  • 格式大致是:"res_model,res_id"

例如可能像:

  • res.partner,42
  • sale.order,17

所以它把“模型名 + id”塞进一个字段里。

这也是为什么它灵活:

  • 模型名是动态的
  • id 也跟着模型走

但这也说明它不像普通 FK 那样可以直接由数据库检查“这个 id 一定属于固定表”。


Many2oneReference 又是什么

它和 Reference 很像,但拆法不同。

源码说明它:

  • 自己在数据库里存一个整数 id
  • 同时要求模型上另有一个 Char 字段保存模型名
  • 通过 model_field 指出“模型名存在哪个字段里”

也就是说,它不是把 model,id 合成一个字符串,而是拆成两列思路:

  • 一列存模型名
  • 一列存 id

所以它和 Reference 的差别,不在于“是不是能跨模型”,而在于:

跨模型引用信息是合在一个字段里,还是拆到两个字段里。


为什么它们都叫“伪关系”

因为它们都没有普通 Many2one 那种强外键语义。

这会带来几个直接后果。

1. 数据库层保护弱得多

普通 Many2one 的一个重要价值,是数据库知道:

  • 这列引用哪个表
  • 可以做 FK 约束

Reference / Many2oneReference 指向的表本身是动态的,数据库很难给你做同等级保护。

2. 合法性更多依赖 ORM 约定

源码里会做一些校验,比如:

  • 模型名是否在允许 selection 里
  • 目标记录是否存在

但这些更多是 ORM 层、应用层的保障,不是数据库天然托底。

3. 开发者更要自己想清“删目标记录以后怎么办”

因为关系不是固定 FK,所以 ondelete 语义、脏引用风险、展示逻辑,都比普通 Many2one 更需要主动设计。


它们分别适合什么场景

Reference 更像“一个字段里装完整通用引用”

适合:

  • 本身就想把完整引用放进一个字段
  • 读写习惯更接近“引用值”
  • 界面或逻辑上把它当成通用资源指针

Many2oneReference 更像“模型名 + id 分列存储”

适合:

  • 模型名本身也需要单独存在
  • 逻辑上会分别使用 model 和 id
  • 某些底层关系操作更适合 id 单独存放

它并不是“更强的 Many2one”,只是另一种伪关系表达方式。


新手最容易误解的点

1. 以为它们只是“能指向多个模型的 Many2one”

这会低估风险。

更准确地说,它们是跨模型引用机制,不是普通 FK 的扩展版。

2. 以为数据库会像 Many2one 一样保你干净

不会同等级地保。

3. 以为选哪个只看界面组件

实际上更该看的是:

  • 你想怎样存储引用
  • 模型名是否要独立存在
  • 后续校验和删除边界如何处理

实战设计时我会怎么判断

先问三件事:

  1. 目标模型是不是天然不固定?
  2. 这个引用如果失效,系统能不能接受?
  3. 我更需要“单字段完整引用”,还是“模型名和 id 分离存储”?

如果第 1 题答案是否定的,那大概率还是老老实实用普通 Many2one 更稳。

因为灵活性不是免费的。


一句话记忆法

ReferenceMany2oneReference 的本质,不是“高级 Many2one”,而是 Odoo 为跨模型引用提供的伪关系字段;它们换来了灵活性,也失去了很多外键级保护。

理解这层,选字段时就不会只看“能不能指过去”,而会开始考虑“指过去以后,数据边界是否还站得住”。

DISCUSSION

评论区

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