update() 不触发 post_save 信号,因其绕过 ORM 实例层直接执行 SQL,不调用 save()、不实例化对象;save() 则触发信号、处理 auto_now 等逻辑,但并发下易覆盖字段,需用 update_fields 避免。
update() 方法根本不会调用模型的 save(),这是设计使然,不是 bug。它绕过 ORM 的实例层,直接拼 SQL 执行 —— 所以信号、auto_now、自定义 save() 逻辑全被跳过。
为什么 update() 不触发 post_save 信号
Django 的 post_save 是在模型实例调用 save() 后才由 ORM 主动分发的。而 QuerySet.update() 走的是纯数据库路径:构造 UPDATE ... SET ... WHERE,不实例化模型对象,自然没有“保存动作”可监听。
- 信号注册再正确(比如用了
AppConfig.ready())、@receiver写得再标准,对update()都无效 - 如果你依赖信号做缓存刷新、日志记录或异步通知,用
update()就等于主动绕开这些逻辑 - 验证方式很简单:在信号函数里加
print()或日志,执行MyModel.objects.filter(...).update(...),什么都不会输出
save() 在并发场景下会覆盖字段值
当多个线程/任务同时操作同一行数据,且都用 instance.save(),就可能丢失更新 —— 因为默认行为是全量写入所有字段,而非只写你改过的那几个。
go语言参考手册 中文CHM版
Go 是一个开源的编程语言,它能让构造简单、可靠且高效的软件变得容易。本文给大家带来Go参考手册,需要的可以来下载! Go是从2007年末由Robert Griesemer, Rob Pike, Ken Thompson主持开发,后来还加入了Ian Lance Taylor, Russ Cox等人,并最终于2009年11月开源,在2012年早些时候发布了Go 1稳定版本。现在Go的开发已经是完全开放的,并且拥有一个活跃的社区。 Go 语言特色 简洁、快速、安全 并行、有趣、开源 内存管理、v数组安全、编译
- 线程 A 读取
user.name="Alice", user.email="a@example.com" - 线程 B 同时读取到同样快照,只改
user.email="b@example.com"并save() - 线程 A 接着改
user.name="Bob"并save(),此时email又被写回旧值"a@example.com" - 解决办法是显式传
update_fields:user.save(update_fields=["name"])或user.save(update_fields=["email"])
什么时候该用 update(),什么时候必须用 save()
关键看你要不要 ORM 实例层的语义:时间字段自动更新、验证逻辑、信号、自定义保存前/后钩子。
- 要
auto_now生效?→ 必须用save(),update()完全无视该选项 - 要避免并发覆盖?→ 用
save(update_fields=[...]),别依赖默认全量更新 - 只更新几十行且需触发信号?→ 循环 +
save()是安全选择 - 更新上万行且无副作用要求?→
update()性能高一个数量级,但得自己补信号或缓存逻辑 - 混合需求(比如批量更新 + 部分行需触发信号)?→ 先
update()大部分,再对少量特殊行单独save()
最常被忽略的一点:update() 返回的是受影响行数,不是模型实例;而 save() 返回的是实例本身 —— 如果后续代码还打算继续链式操作这个对象,用错方法会导致 AttributeError。