无论您选择下面的哪个选项,SQLAlchemy 都会附带关于的一个重大警告after_commit event http://docs.sqlalchemy.org/en/latest/orm/events.html#sqlalchemy.orm.events.SessionEvents.after_commit(这是两种方式都发送信号的时候)。
当会话不处于活动事务中时after_commit()
事件被调用,因此无法发出 SQL.
如果您的回调需要查询或提交到数据库,则可能会出现意外问题。在这种情况下,您可以使用任务队列,例如Celery http://www.celeryproject.org/在后台线程中执行此操作(使用单独的会话)。无论如何,这可能是正确的方法,因为发送电子邮件需要很长时间,并且您不希望视图在发送电子邮件时等待返回。
Flask-SQLAlchemy提供信号 http://flask-sqlalchemy.pocoo.org/dev/signals/#models_committed您可以收听发送所有插入/更新/删除操作的声音。需要通过设置来启用app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = True
因为跟踪修改的成本很高,并且在大多数情况下不需要。
然后监听信号:
from flask_sqlalchemy import models_committed
def notify_subscribers(app, changes):
new_posts = [target for target, op in changes if isinstance(target, Post) and op in ('insert', 'update')]
# notify about the new and updated posts
models_committed.connect(notify_subscribers, app)
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = True
您也可以自己实现(主要是通过从 Flask-SQLAlchemy 复制代码)。这有点棘手,因为模型更改发生在flush, 不开commit,因此您需要在刷新发生时记录所有更改,然后在提交后使用它们。
from sqlalchemy import event
class ModelChangeEvent(object):
def __init__(self, session, *callbacks):
self.model_changes = {}
self.callbacks = callbacks
event.listen(session, 'before_flush', self.record_ops)
event.listen(session, 'before_commit', self.record_ops)
event.listen(session, 'after_commit', self.after_commit)
event.listen(session, 'after_rollback', self.after_rollback)
def record_ops(self, session, flush_context=None, instances=None):
for targets, operation in ((session.new, 'insert'), (session.dirty, 'update'), (session.deleted, 'delete')):
for target in targets:
state = inspect(target)
key = state.identity_key if state.has_identity else id(target)
self.model_changes[key] = (target, operation)
def after_commit(self, session):
if self._model_changes:
changes = list(self.model_changes.values())
for callback in self.callbacks:
callback(changes=changes)
self.model_changes.clear()
def after_rollback(self, session):
self.model_changes.clear()
def notify_subscribers(changes):
new_posts = [target for target, op in changes if isinstance(target, Post) and op in ('insert', 'update')]
# notify about new and updated posts
# pass all the callbacks (if you have more than notify_subscribers)
mce = ModelChangeEvent(db.session, notify_subscribers)
# or you can append more callbacks
mce.callbacks.append(my_other_callback)