post_save仅在Model.save()、create()及显式启用send_post_save_signal=True的bulk_create()后触发,不响应update()、bulk_update()或原生SQL;需在apps.py ready()中注册,避免同步耗时操作,异步任务应通过Celery等解耦。

信号机制不是万能解耦方案,它容易导致隐式调用、调试困难、事务边界不清——除非你明确需要在模型保存/删除等生命周期节点触发旁路逻辑,否则优先考虑显式调用或领域事件。
什么时候该用 post_save 而不是手动写钩子?
当你需要在模型实例成功写入数据库后,触发与主业务无关的副作用(如发通知、更新统计缓存、记录日志),且这些操作不应阻塞主流程或影响事务一致性时,post_save 是合理选择。但它只在 model.save() 或 QuerySet.bulk_create()(需额外处理)中触发,不响应 raw SQL 或 admin 批量操作。
-
sender必须精确指定模型类,不能用字符串(如'myapp.User')——否则接收器不会被调用 -
created参数只对post_save有效,pre_save没有该字段 - 若在事务中调用
save(),信号处理器也运行在同个事务内;想脱离事务(如发邮件),得用transaction.on_commit()包裹
disconnect 为什么总被忽略?
信号是全局注册的,模块重复导入、测试中反复 setup/teardown、热重载(如 Django runserver)都可能造成同一个处理器被多次连接,引发重复执行。常见错误是只在 apps.py 的 ready() 中 connect,却没配对 disconnect。
仅使用 Python 将 Claw Code 虚拟化框架与 OpenClaw 集成,提供 184 种工具和 200 多条命令,用于安全分析和代码质量...
- 在测试 teardown 阶段主动调用
receiver.disconnect(sender=MyModel) - 使用装饰器模式时,保留对 receiver 函数的引用,避免匿名 lambda 导致无法 disconnect
- Django 4.2+ 支持
weak=False参数,但默认 weak=True —— 若 receiver 是局部函数或闭包,可能被 GC 掉而自动断连,行为不稳定
bulk_create 会触发 post_save 吗?
不会。Django 的 bulk_create 绕过单条模型的 save() 流程,因此不触发任何 save 相关信号。这是性能优化的代价,也是最常踩的坑。
立即学习“Python免费学习笔记(深入)”;
- 若必须响应批量创建,改用循环
create()(牺牲性能)或在 bulk 后手动调用post_save.send()(需传入instance和created=True) - 更稳妥的做法:把“需响应批量操作”的逻辑抽离为独立函数,在 bulk_create 后显式调用,而非依赖信号
- 注意
bulk_update同样不触发信号,且没有post_update内置信号可替代
信号真正的复杂点不在注册和触发,而在它抹平了调用栈——你无法从 traceback 看出某段日志是谁触发的,也无法用 IDE “Find Usages” 定位接收器。一旦项目里信号超过 5 个,就得靠文档或全局搜索 @receiver 来厘清依赖链。

















