先说结论
在 Odoo 里,这四个词经常一起出现,但它们不是同一件事:
- compute:负责计算字段值
- onchange:负责表单交互时的即时联动
- constraints:负责保存时的业务校验
- recompute:负责把需要存储的 computed field 重新算出来
如果把它们混成一类,你会很难解释:
- 为什么表单里改了值,没保存就能看到变化
- 为什么保存时才报错
- 为什么数据库里某些存储字段会自动重算
其实这四个机制是 Odoo ORM 分层设计的结果。
onchange:给表单用的“即时反馈”
_onchange_methods 会扫描模型上的 onchange 方法,并把它们挂到对应字段上。
它的定位非常明确:
用户在前端表单改了一个字段,系统要不要立刻帮他改别的字段?
典型场景是:
- 选了客户后自动带出付款条款
- 改了产品后自动更新税、UoM、价格
- 变更某个选项后刷新一批界面字段
所以 onchange 是前端交互层的帮助,不是最终入库逻辑。
constraints:保存时才真正拦你
_constraint_methods 收集的是 Python 约束方法。
它们会在写入 / 创建时参与校验。
它回答的问题是:
这条业务数据能不能真的落库?
例如:
- 金额不能为负
- 某些字段组合不允许同时为空
- 某种配置下,字段值必须在允许范围内
这就是为什么 constraints 更像“门禁”,而不是“界面助手”。
compute:字段值本身由逻辑决定
compute 讲的是字段设计,而不是界面事件。 一个 computed field 的值,应该由它的依赖决定,而不是由用户手工随便写。
在 Odoo 里,这种机制的好处是:
- 业务规则集中
- 状态变化可重复计算
- 相关字段不会到处写重复逻辑
所以 compute 更像“字段自身的定义方式”。
recompute:专门处理存储型计算字段
_recompute_model()、_recompute_recordset() 和 _recompute_field() 做的是一件更底层的事:
把已经排进待计算队列的存储字段重新算一遍。
这和 onchange 完全不是一个层次。
recompute 关注的是:
- 哪些字段已经被标记为需要重算
- 哪些 record 还在待处理队列里
- 哪些 compute 字段是
store=True
换句话说,recompute 是 ORM 的“后台补算器”。
为什么它们不能互相替代
1. onchange 不能替代 constraints
因为 onchange 只发生在界面交互里。 如果数据通过导入、RPC、批量脚本写入,onchange 不一定会帮你兜底。
2. constraints 不能替代 onchange
因为 constraints 是保存时才校验。 它不能像 onchange 那样给用户即时反馈。
3. compute 不能替代 recompute
compute 是字段定义方式,recompute 是执行机制。 你定义了 computed field,不代表它会自动在任何时刻都重新算。
4. recompute 不能替代 onchange
recompute 是后台机制,不是表单交互体验。
一个简单的记忆法
你可以把它们记成四个动作:
- onchange:我改了,界面马上跟着动
- constraints:我点保存时,系统拦不拦
- compute:这个字段理论上应该怎么算
- recompute:已经入队的存储字段现在重新算
这个分类一旦建立,很多 ORM 现象就不会混了。
代码里最值得注意的点
_onchange_methods 和 _constraint_methods 都是在模型 setup 后按方法扫描出来的。
这意味着它们不是运行时临时“猜”的,而是模型元数据的一部分。
另一方面,_recompute_field() 只会处理 transaction 里已经在 tocompute 队列中的字段。
这也解释了为什么某些字段变了,但你没有马上看到最终值:
不是它不支持自动化,而是它在等 ORM 把队列处理完。
新手最容易误解的 4 件事
1. “onchange 只要写了,数据库里也一定生效”
不对。它主要服务于表单交互。
2. “constraints 会在用户每次点字段时就触发”
也不对。它通常在 create/write 的保存过程里才发挥作用。
3. “compute 字段一定是只读”
通常是,但真正关键的是它的值来源于计算逻辑,而不是手工输入。
4. “recompute 就是 onchange 的后台版”
不是。它们解决的是完全不同的问题。
最后一句话
Odoo ORM 的这四个机制,分别管的是:
- 界面联动
- 保存校验
- 字段定义
- 存储重算
把它们分开理解,模型逻辑就会清楚很多,也更容易调试。
DISCUSSION
评论区