Django不监听数据库变更,仅响应自身ORM操作;post_save/post_delete可捕获模型层变更,但QuerySet.update()等批量操作不触发信号,需手动补救或下沉至数据库触发器。

Django 本身不监听数据库变更,它只响应自己发起的 ORM 操作 —— 这是关键前提。MySQL、PostgreSQL 等底层数据库不会主动通知 Django “某行被 update 了”,除非你绕过 Django 直接执行 SQL 或其他服务改库。所以真正在用的方案,是“在 Django 写库时埋钩子”,而不是“监听数据库日志”。
用 post_save 和 post_delete 捕获模型层变更
这是最常用、最可控的方式:Django 的信号机制会在 ORM 层完成保存或删除后触发回调,适合做审计、缓存刷新、触发异步任务等。
-
post_save触发时机是model_instance.save()或Model.objects.create()成功后,但注意:批量更新(如QuerySet.update())不会触发该信号 -
post_delete同理,只对instance.delete()生效,QuerySet.delete()也不触发 - 必须确保信号接收函数在 Django 启动时已导入(常见坑:放在 views.py 里但没被任何模块引用,信号就不会注册)
- 示例中用
kwargs['created']区分新增/更新,但别依赖它判断字段是否“真正变了”——比如只改了个无关字段,created==False仍为 True,但业务上可能不需要响应
避开 QuerySet.update() 导致的信号失效
当你要高效更新大量记录时,QuerySet.update() 是性能首选,但它绕过模型实例和信号。如果业务逻辑必须响应这类变更,有三个实际选择:
- 放弃批量更新,改用循环
for obj in qs: obj.field = x; obj.save()(仅适用于几百条以内,否则 I/O 压力大) - 在调用
update()后,手动触发等效逻辑,比如:MyModel.objects.filter(...).update(status='done'); handle_status_change.delay(...) - 把需要响应的字段变更逻辑下沉到数据库层,例如 MySQL 的触发器 + 写入一个变更日志表,再由单独进程轮询该日志表(脱离 Django 信号体系,但能覆盖所有写入源)
不要试图监听 MySQL binlog 或 PostgreSQL logical replication
虽然技术上可行(用 mysql-binlog-connector-python 或 pglogrepl),但在 Django 项目中属于过度设计:
- 引入额外服务依赖和运维复杂度,和 Django 的“应用层控制”哲学相悖
- 无法区分变更来源:是 Django 写的?还是 DBA 手动 SQL?或是另一个微服务?业务上往往需要明确上下文
- binlog 解析失败、位点丢失、事务回滚等情况需自行容错,稳定性和调试成本远高于信号机制
- 若真有跨服务数据同步需求,应优先考虑消息队列(如 Kafka、RabbitMQ)由各服务主动发布事件,而非被动解析日志
信号机制的边界很清晰:它只管 Django 自己干的事。一旦你允许直连数据库、允许其他服务写同一张表、或者重度依赖 update(),那“监听变更”这件事就必须拆成多层策略来应对,不能指望一个装饰器解决所有问题。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











