表单边界

Odoo 的 onchange 为什么只改表单,不负责落库

从虚拟记录、RPC 回传和表单缓存解释 onchange 的边界,并说明它为何不能替代 compute、inverse 和 constraints。

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

先说结论

@api.onchange 的定位只有一个:帮表单在用户输入时即时调整其他字段

它不是保存逻辑,不是数据库触发器,也不是一条必经的业务规则。

换句话说:

onchange 负责“界面上看起来对不对”,不是“数据库里一定写成什么”。

这也是很多新手第一次写 Odoo 时最容易踩的坑。


源码里,onchange 本来就是给表单准备的

odoo/orm/models.py 里,Odoo 会先收集模型上的 onchange 方法,生成一个映射表。

你能从代码里看到两个关键信号:

  1. @api.onchange 方法会被登记到 _onchange_methods
  2. 真正的 onchange() 接口并不在 ORM 核心实现,而是由 Web 层来负责调用和回传

这说明它的设计目标从一开始就不是“直接改数据库”,而是:

  • 接收前端当前草稿值
  • 运行 onchange 方法
  • 把新值、warning、domain 等结果返回给页面

所以它天然属于表单交互层


为什么它不会自动落库

因为用户此时通常还没有点“保存”。

表单里的记录可能还是一个虚拟草稿:

  • 你改了 partner_id
  • 系统顺手帮你填了 payment_term_id
  • 也可能改了 currency_idjournal_idtax_ids

这些变化先停留在客户端的表单缓存里,直到用户提交 create/write,才会真正写进数据库。

所以 onchange 的正确理解不是“改数据”,而是:

对当前草稿做即时补全。


onchage、compute、inverse、constraints 各管什么

把它们分开,会非常清楚:

onchange

只管表单交互。用户改字段时即时补值。

compute

只管字段的计算来源。它能在读取、重算、保存后保持一致。

inverse

当一个 computed field 需要用户反向编辑时,用它把值写回源字段。

constraints

只管校验。它不负责帮你自动改值,只负责在不合法时拦住。

如果你想让一个值“最终一定存在数据库里”,不要依赖 onchange。


一个很实用的判断题

问自己一句:

这个逻辑是不是只在“表单编辑过程中”有意义?

如果答案是“是”,那就可以考虑 onchange。

例如:

  • 选了客户后,自动填付款条款
  • 选了产品后,自动填税、单位、描述
  • 改了公司后,顺手切换可用的 journal

如果答案是“不是”,比如:

  • 创建后必须永久存在
  • 批处理、导入、RPC 都必须一致
  • 任何绕过表单的写入也要生效

那就应该放在 create() / write() / compute() / constraints() 里,而不是 onchange。


源码里还有一个容易忽略的小点:change_default

_onchange_methods 里不只会收集 @api.onchange,还会把某些字段的 change_default 也塞进去。

这意味着:

  • 一些默认值变化,也会被当成“表单联动”处理
  • onchange 不只是你手写的方法,还包含框架级的默认值联动

也就是说,表单里的自动补值,很多不是“业务代码神奇地跑了”,而是 ORM 和 Web 在协作。


实战里最常见的误区

1. 用 onchange 做必填校验

不稳。用户可能直接导入、写 RPC、跑脚本,根本不会经过表单事件。

2. 用 onchange 做核心业务计算

如果这个值需要被搜索、报表、后续逻辑依赖,应该用 compute 或真正落库的逻辑。

3. 以为 onchange 已经保存成功

没有。它只是返回一份“页面应该怎么变”的建议。


一个好用的记忆方式

把 onchange 想成“前台助手”:

  • 负责提醒你
  • 帮你自动填几项
  • 帮你减少手工输入
  • 但不负责最终签字入档

create() / write() 才是“签字入档”的那一步。


结尾

如果你只记一句话,那就记这句:

onchange 让表单更聪明,compute / inverse / constraints 让数据更可靠。

把这条边界守住,很多表单联动问题都会少一半。

DISCUSSION

评论区

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