且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

提交某种类型的模型后运行功能

更新时间:2023-11-29 08:46:52

无论您在下面选择哪个选项,SQLAlchemy都带有

No matter which option you chose below, SQLAlchemy comes with a big warning about the after_commit event (which is when both ways send the signal).

当调用after_commit()事件时,会话不在活动事务中,因此无法发出SQL .

The Session is not in an active transaction when the after_commit() event is invoked, and therefore can not emit SQL.

如果您的回调需要查询或提交到数据库,则可能有意外问题.在这种情况下,您可以使用任务队列(例如 Celery )在后台线程中执行此任务(使用单独的会话).无论如何,这可能是正确的方法,因为发送电子邮件会花费很长时间,并且您不希望视图在发生这种情况时等待返回.

If your callback needs to query or commit to the database, it may have unexpected issues. In this case, you could use a task queue such as Celery to execute this in a background thread (with a separate session). This is probably the right way to go anyway, since sending emails takes a long time and you don't want your view to wait to return while it's happening.

Flask-SQLAlchemy 提供了一个信号,您可以收听发送的信号所有插入/更新/删除操作.需要通过设置app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = True启用它,因为跟踪修改很昂贵,并且在大多数情况下不需要.

Flask-SQLAlchemy provides a signal you can listen to that sends all the insert/update/delete ops. It needs to be enabled by setting app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = True because tracking modifications is expensive and not needed in most cases.

然后监听信号:

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 上,因此您需要记录所有发生的更改,然后在提交后使用它们.


You can also implement this yourself (mostly by copying the code from Flask-SQLAlchemy). It's slightly tricky, because model changes occur on flush, not on commit, so you need to record all changes as flushes occur, then use them after the 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)