2011-12-30 10 views
6

Usando il tornado, voglio creare un po 'di magia del middleware che assicuri che le mie sessioni SQLAlchemy vengano correttamente chiuse/pulite in modo che gli oggetti non vengano condivisi da una richiesta all'altra. Il trucco è che, dal momento che alcuni dei miei gestori di tornado sono asincroni, non posso condividere una sola sessione per ogni richiesta.SQLAlchemy + Tornado: come creare un scopefunc per ScopedSession di SQLAlchemy?

Quindi sono rimasto a provare a creare una ScopedSession che sa come creare una nuova sessione per ogni richiesta. Tutto quello che devo fare è definire un scopefunc per il mio codice che può trasformare la richiesta attualmente in esecuzione in una chiave univoca di qualche tipo, tuttavia non riesco a capire come ottenere la richiesta corrente in qualsiasi momento (al di fuori dell'ambito dell'attuale RequestHandler, a cui la mia funzione non ha accesso).

C'è qualcosa che posso fare per farlo funzionare?

+2

Non conosco il tornado ma è possibile associare la sessione alla richiesta stessa (ad esempio, non utilizzare scopedsession se non è conveniente). allora puoi semplicemente dire request.session. Ha ancora bisogno di avere ami all'inizio/fine per l'installazione/demolizione. – zzzeek

+0

@zzzeek Se pubblichi questo come risposta, lo contrassegnerò come corretto! Più ci pensavo, più mi rendevo conto che hai ragione - questo è il modo più veloce e facile da capire per ottenere ciò di cui ho bisogno. Grazie! –

+0

Ho pensato allo stesso modo, condividere una sessione creata tramite scopped_session tra tutti i gestori asincroni creerebbe incoerenza, giusto? Dire che in un gestore potrei chiamare scopped_session.remove() alla fine del gestore ma l'altro gestore (che funziona in modo asincrono) potrebbe ancora usarlo! – giga

risposta

4

È possibile associare lo Session alla richiesta stessa (ad esempio, non utilizzare scopedsession se non è conveniente). Quindi puoi semplicemente dire, request.session. Ha ancora bisogno di avere ami all'inizio/fine per l'installazione/smontaggio.

edit: personalizzato funzione di scoping

def get_current_tornado_request(): 
    # TODO: ask on the Tornado mailing list how 
    # to acquire the request currently being invoked 

Session = scoped_session(sessionmaker(), scopefunc=get_current_tornado_request) 
+0

Quindi in pratica quello che dovrei fare è creare una sessione all'inizio di una richiesta e assegnarla al gestore della richiesta. ma ho bisogno di chiamare molti metodi che richiedono la sessione, ho bisogno di passare il gestore della richiesta o la sessione stessa tra di loro ... codice che diventa brutto :( – giga

+0

quindi utilizzare una scopedsession con una funzione di ambito personalizzata. – zzzeek

+0

Puoi spiegarci di più su come scrivere una funzione di scoping personalizzata ... grazie, sono nuovo in Python – giga

0

(Questo è un 2017 risposta ad una domanda 2011) Come @Stefano Borini ha sottolineato, in modo più semplice Tornado 4 è quello di lasciare solo il RequestHandler implicitamente pass the session around. Tornado sarà tenere traccia dello stato dell'istanza del gestore quando si utilizza coroutine modelli decoratore:

import logging 

_logger = logging.getLogger(__name__) 

from sqlalchemy import create_engine, exc as sqla_exc 
from sqlalchemy.orm import sessionmaker, exc as orm_exc 

from tornado import gen 
from tornado.web import RequestHandler 

from my_models import SQLA_Class 

Session = sessionmaker(bind=create_engine(...)) 

class BaseHandler(RequestHandler): 

    @gen.coroutine 
    def prepare(): 
     self.db_session = Session() 

    def on_finish(): 
     self.db_session.close() 

class MyHander(BaseHandler): 

    @gen.coroutine 
    def post(): 
     SQLA_Object = self.db_session.query(SQLA_Class)... 
     SQLA_Object.attribute = ... 

     try: 
      db_session.commit() 
     except sqla_exc.SQLAlchemyError: 
      _logger.exception("Couldn't commit") 
      db_session.rollback() 

Se davvero davvero necessario fare riferimento in modo asincrono una sessione di SQL Alchemy all'interno di un declarative_base (che riterrei un anti-modello dal momento che over-coppie i modello per l'applicazione), Amit Matani ha un esempio non funzionante here.