先说结论
在 Odoo 里,“删除一条记录”从来都不只是数据库层面的一个小动作。
它同时可能影响:
- 业务流程完整性
- 关系对象一致性
- 审计留痕
- 用户是否还能追溯历史
所以 unlink() 和 ondelete 真正讨论的不是“怎么删”,而是:
什么可以删、删了以后谁跟着变、哪些场景根本不应该物理删除。
为什么删除问题总是高风险
因为创建和修改通常是在“增加信息”, 而删除是在“直接拿走一段事实”。
一旦删错,常见后果是:
- 业务链断掉
- 下游引用失效
- 报表不一致
- 用户以为系统丢数据
这也是为什么成熟系统往往对删除特别保守。
Odoo 也一样。
unlink() 本质上代表什么
unlink() 最直接的作用当然是删除记录。
但从业务层看,它更像:
正式请求把这条业务对象从系统里移除。
这意味着它通常不该只是“我能删就删”。
很多模型在 unlink() 里都会加入业务保护,比如:
- 已确认单据不允许删
- 已过账凭证不允许删
- 已完成流程不允许删
- 有下游引用时不允许删
所以 unlink() 往往也是业务边界的一部分。
ondelete 在解决什么问题
ondelete 更偏向关系层。
它关心的是:
- 当被引用对象删除时
- 这条关系应该怎么办
常见语义你可以先理解成:
- restrict:不让你删
- cascade:跟着删
- set null:关系断开,引用置空
所以 ondelete 不是在问“删不删当前对象”,而是在问:
如果上游对象消失了,下游关系该如何收尾。
为什么 cascade 很方便但也危险
因为它省事。
上游一删,下游跟着清掉,看起来很干脆。
但危险在于:
- 你可能低估了波及范围
- 一次删除会级联带走很多数据
- 用户可能根本不知道后面还有哪些依赖对象
所以 cascade 适合那些真正属于“强从属、无独立业务意义”的子对象。
如果子对象本身已经承载业务事实,就要非常慎重。
为什么很多业务对象不该物理删除
因为很多记录一旦进入正式业务阶段,它的价值已经不只是“当前还用不用”。
它还可能是:
- 历史依据
- 审计线索
- 报表来源
- 责任追踪依据
这时更稳的做法往往不是删,而是:
- 归档
- 取消
- 作废
- 关闭状态
也就是说:
很多业务对象真正需要的是“退出使用”,不是“从世界上抹掉”。
实战里最容易踩的 5 个坑
1. 把删除当成普通清理动作
结果误删业务历史。
2. cascade 用得太随意
一个删除带崩整条链。
3. 本该归档的对象硬删
后面报表和追溯都出问题。
4. 只看数据库约束,不看业务边界
技术能删,不代表业务该删。
5. 在 unlink() 里忘了考虑下游引用和状态
这类 bug 很隐蔽,也很伤。
一个特别实用的判断方法
设计删除策略时,先问这几个问题:
1. 这条记录是不是正式业务事实?
如果是,优先考虑不物理删除。
2. 下游对象有没有独立业务意义?
如果有,别轻易 cascade。
3. 用户真正想要的是“消失”,还是“停用/作废”?
很多需求其实不是删。
4. 删掉之后,审计、报表、追溯还能不能站得住?
这一步非常关键。
一句话记忆法
把这套机制记成一句话:
unlink()决定当前对象能不能被删,ondelete决定关联对象在上游删除后怎么收尾,而成熟业务对象很多时候更该归档或作废,而不是物理删除。
理解这一句,Odoo 删除策略会稳很多。
DISCUSSION
评论区