2015-09-03 4 views
6

Desidero eseguire una funzione quando vengono eseguite le istanze del modello Post. Voglio eseguirlo ogni ora impegnata, quindi preferisco non chiamare esplicitamente la funzione ovunque. Come posso fare questo?Esegui funzione dopo il commit di un determinato tipo di modello

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. 

risposta

3

Non importa quale opzione si è scelto di seguito, viene fornito con SQLAlchemy a big warning about the after_commit event (che è quando entrambi i modi inviano il segnale).

la sessione non è in una transazione attiva quando l'evento after_commit() viene richiamato, e pertanto non può emettere SQL.

Se il callback deve eseguire query o eseguire il commit nel database, potrebbero verificarsi problemi imprevisti. In questo caso, è possibile utilizzare una coda di attività come Celery per eseguirla in un thread in background (con una sessione separata). Questo è probabilmente il modo giusto per andare comunque, poiché l'invio di email richiede molto tempo e non vuoi che la tua vista attenda di tornare mentre sta accadendo.


Flask-SQLAlchemy provides a signal è possibile ascoltare che invia tutti i inserimento/aggiornamento/cancellare ops. Tuttavia, the patch che rende questo lavoro effettivamente non ancora nella versione rilasciata, dovresti installare la versione di sviluppo da git (il segnale esiste nella versione corrente (2.0), ma non funziona correttamente). Se siete d'accordo con questo, quindi installarlo con:

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

poi ascoltare il segnale:

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) 

È possibile anche implementare da soli (per lo più copiando il codice da Flask -SQLAlchemy). È un po 'complicato, perché le modifiche al modello si verificano su flush, non su commit, quindi è necessario registrare tutte le modifiche mentre si verificano gli svuotamenti, quindi utilizzarli dopo il 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) 
Problemi correlati