post_save 里发通知、发消息、触发异步任务,是最常见的用法,也是最容易出事故的地方:信号是在事务提交之前触发的。这篇在真实的 PostgreSQL 上用第二个连接去「偷看」,把这件事量出来。
post_save 触发:另一个连接能看到这一行吗?False create() 返回 on_commit 回调:另一个连接能看到吗?True atomic 块退出(已提交)
可见性用「另开一个线程、用它自己的数据库连接去查」来测,这是同一个数据库上的第二个会话。异步任务(比如 Celery worker)就是这样的第二个会话:如果在 post_save 里就把任务发出去,worker 可能在事务提交前就来查这行数据,结果查不到。
事务回滚后,库里这个订单存在吗: False | 外部通知列表: ['已通知外部系统:订单 2 创建'] on_commit 版本的回调有没有执行: 没有(回滚后不会执行)
post_save 里做的外部调用不会因回滚而撤销。
回滚: [] 提交: ['提交后通知:订单 4']
transaction.on_commit(func) 把回调挂到当前事务上:提交后执行,回滚则丢弃。
post_save 触发:另一个连接能看到这一行吗?True on_commit 回调:另一个连接能看到吗?True
autocommit 模式下 on_commit 的回调会立即执行,所以写 receiver 时不必区分有没有外层事务,统一用 on_commit 即可。