SQLAlchemy事件监听仅捕获当前Python进程通过ORM触发的操作,如before_insert、before_update、after_commit等,不监听数据库底层变更;需跨服务感知变动时应配合消息队列或CDC工具。

SQLAlchemy事件监听能捕获哪些数据库变动
SQLAlchemy的事件系统不监听真实数据库层面的变更(比如其他服务直连MySQL执行的UPDATE),只响应当前Python进程通过session.add()、session.delete()、session.commit()等触发的ORM对象生命周期变化。换句话说,它监听的是“ORM操作”,不是“数据库binlog”。如果你需要跨服务感知变更,得另配消息队列或数据库CDC工具。
监听对象创建、更新、删除的正确写法
用sqlalchemy.event.listen()绑定到Mapper或Session级别事件,最常用的是before_insert、before_update、before_delete——它们在flush阶段触发,此时对象已赋值但尚未提交,适合做校验或补充字段:
from sqlalchemy import event from myapp.models import User <p>@event.listens_for(User, 'before_insert') def set_created_at(mapper, connection, target): if not target.created_at: target.created_at = datetime.utcnow()</p><p>@event.listens_for(User, 'before_update') def set_updated_at(mapper, connection, target): target.updated_at = datetime.utcnow() </p>
- 不要在
before_*里调用session.commit()——会引发递归或事务嵌套错误 -
target是ORM实例,不是原始SQL行;connection可用于执行底层SQL,但需注意隔离级别 - 若监听多个模型,避免全局注册,推荐按模块集中管理监听器,防止导入顺序引发漏绑
监听commit成功后发通知的坑点
想在事务真正落库后触发外部动作(如发MQ、更新缓存),必须用after_commit事件,但它只绑定到Session实例,不能直接绑在类上:
from sqlalchemy.orm import Session
<p>def on_after_commit(session):
for obj in session.new:
if isinstance(obj, User):
print(f"User created: {obj.id}")
for obj in session.deleted:
if isinstance(obj, User):
print(f"User deleted: {obj.id}")</p><h1>必须对每个session实例绑定,Flask-SQLAlchemy中通常在请求结束时做</h1><p>@event.listens_for(Session, 'after_commit')
def receive_after_commit(session):
on_after_commit(session)
</p>
- Flask-SQLAlchemy默认使用scoped_session,
Session是类,不是实例,所以这个监听器会生效 -
session.dirty里的对象状态可能已是“已刷新”,无法可靠判断字段是否真被修改——需要自己实现__init__快照或用inspect(obj).attrs.xxx.history - 如果应用启用了多线程或多worker(如gunicorn),
after_commit里的网络调用必须异步或丢进任务队列,否则阻塞主线程
为什么after_flush比after_commit更难控制
after_flush在session.flush()后触发,早于commit,常被误用来“替代”after_commit。但它有硬伤:事务可能回滚,此时你发的通知就失效了,且session.identity_map里对象的id可能还是None(尤其自增主键未刷出):
- 别在
after_flush里发MQ或改Redis——没有原子性保障 - 如果必须用,先检查
session.is_active和obj.id is not None,再加一层数据库级幂等标记 - Flask中
after_flush和after_commit都可能被多次触发(比如bulk insert),注意去重逻辑要放在业务层,别依赖事件触发次数
实际项目里,90%的“监听数据库变动”需求其实只需要before_*做数据准备 + after_commit做最终通知,after_flush留给极少数需要拦截SQL生成的场景,比如动态分表路由。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











