2015-09-03 4 views
6

Ich möchte eine Funktion ausführen, wenn Instanzen des Post-Modells festgeschrieben sind. Ich möchte es jeder Zeit ausführen sie sind verpflichtet, also würde ich lieber nicht explizit die Funktion überall aufrufen. Wie kann ich das machen?Ausführen der Funktion, nachdem ein bestimmter Typ des Modells festgeschrieben wurde

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. 

Antwort

3

Egal, welche Option Sie unten gewählt haben, kommt SQLAlchemy mit a big warning about the after_commit event (das ist, wenn beide Möglichkeiten, um das Signal zu senden).

die Sitzung nicht in einer aktiven Transaktion, wenn das after_commit() Ereignis aufgerufen wird, und kann daher nicht emittieren SQL.

Wenn Ihr Rückruf Abfragen oder Commit für die Datenbank ausführen muss, können unerwartete Probleme auftreten. In diesem Fall könnten Sie eine Aufgabenwarteschlange wie Celery verwenden, um dies in einem Hintergrundthread (mit einer separaten Sitzung) auszuführen. Dies ist wahrscheinlich der richtige Weg, da das Senden von E-Mails sehr lange dauert und Sie nicht möchten, dass Ihre Ansicht wartet, um zurückzukehren, während sie stattfindet.


Flask-SQLAlchemy provides a signal Sie das hören sendet alle insert/update/delete ops. Allerdings, the patch, dass dies tatsächlich funktioniert, ist nicht in der veröffentlichten Version noch, müssten Sie die dev-Version von git installieren (das Signal existiert in der aktuellen Version (2.0), funktioniert aber nicht richtig). Wenn Sie mit dem ok sind, installieren Sie es dann mit:

pip install https://github.com/mitsuhiko/flask-sqlalchemy/tarball/master 

Dann für das Signal hören:

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) 

Sie auch diese selbst (vor allem durch Kopieren Sie den Code aus Flask implementieren -SQLAlchemy). Es ist ein wenig schwierig, weil Modelländerungen auftreten Flush, nicht auf Commit, so müssen Sie alle Änderungen als Flushes aufzeichnen, dann verwenden Sie sie nach dem Festschreiben.

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) 
Verwandte Themen