【问题标题】:Run function after a certain type of model is committed提交某种类型的模型后运行函数
【发布时间】:2015-09-03 14:47:00
【问题描述】:

我想在Post 模型的实例被提交时运行一个函数。我想在他们提交时运行它任何,所以我宁愿不要在任何地方显式调用该函数。我该怎么做?

def notify_subscribers(post):
    """ send email to subscribers """
    ...

post = Post("Hello World", "This is my first blog entry.")
session.commit() # How to run notify_subscribers with post as argument
                 # as soon as post is committed successfully?

post.title = "Hello World!!1"
session.commit() # Run notify_subscribers once again.

【问题讨论】:

    标签: python flask sqlalchemy flask-sqlalchemy


    【解决方案1】:

    无论您在下面选择哪个选项,SQLAlchemy 都会附带a big warning about the after_commit event(这是双向发送信号的时候)。

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

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


    Flask-SQLAlchemy provides a signal 你可以听它发送所有的插入/更新/删除操作。需要通过设置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)
    

    【讨论】:

      猜你喜欢
      • 2020-01-26
      • 1970-01-01
      • 2015-08-01
      • 1970-01-01
      • 2013-10-10
      • 2013-12-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多