2013-02-10 3 views
11

Ich habe eine Python-Anwendung, die viele kleine Datenbankzugriffsfunktionen hat, mit sqlalchemy. Ich versuche zu vermeiden, dass viele Code Session Session Codes um diese Funktionen herumliegen.Vermeidung von BoilerPlate Session Handling Code in Sqlalchemy Funktionen

Ich habe zahlreiche Funktionen, die wie folgt aussehen:

def get_ticket_history(Session, ticket_id): 
    s = Session() 
    try: 
     rows = s.query(TicketHistory)\ 
       .filter(TicketHistory.ticket_fk==ticket_id)\ 
       .order_by(TicketHistory.id.desc()).all() 
     s.commit() 
     return rows 
    except: 
     s.rollback() 
     raise 
    finally: 
     s.close() 

ich diese Funktionen Refactoring bin versucht, aber nicht sicher, ich habe die beste Ansatz noch. Das Beste, was ich derzeit habe, ist der folgende:

def execute(Session, fn, *args, **kwargs): 
    s = Session() 
    try: 
     ret = fn(s, *args, **kwargs) 
     s.commit() 
     return ret 
    except: 
     s.rollback() 
     raise 
    finally: 
     s.close() 

def get_ticket_history(self, ticket_id): 
    def sql_fn(s): 
     return s.query(TicketHistory)\ 
       .filter(TicketHistory.ticket_fk==ticket_id)\ 
       .order_by(TicketHistory.id.desc()).all() 
    return execute(self.sentinel_session, sql_fn) 

Gibt es einen besseren oder mehr idiomatischen Weg, dies zu tun? Vielleicht mit einem Dekorateur?

Danke, Jon

+1

Ein 'context manager' wäre ein sehr guter Weg. –

Antwort

0

morphyn Vorschlag einen Kontext-Manager zu verwenden ist gut. Sie könnten einen solchen Kontext-Manager erstellen, indem Sie den contextlib.contextmanager Dekorator auf eine Funktion anwenden, die Ihrer ersten get_ticket_history sehr ähnlich ist, indem Sie den Code zwischen try und exception mit einer yield-Anweisung ersetzen und umbenennen, sagen wir transaction. PEP 343 hat ein nahezu identisches Beispiel für diesen Namen.

Verwenden Sie dann diesen Kontextmanager mit der with-Anweisung, um get_ticket_history neu zu implementieren. Es sieht aus wie SQLAlchemy bereits diese Funktion bietet, obwohl, wie Verfahren begin:

http://docs.sqlalchemy.org/en/rel_0_8/orm/session.html#autocommit-mode

14

Die SQLAlchemy docs eine mögliche Art und Weise präsentieren diese Manager mit Kontext zu tun.

http://docs.sqlalchemy.org/en/latest/orm/session_basics.html#when-do-i-construct-a-session-when-do-i-commit-it-and-when-do-i-close-it

Kopieren Sie den Code-Schnipsel hier der Vollständigkeit halber:

from contextlib import contextmanager 

@contextmanager 
def session_scope(): 
    """Provide a transactional scope around a series of operations.""" 
    session = Session() 
    try: 
     yield session 
     session.commit() 
    except: 
     session.rollback() 
     raise 
    finally: 
     session.close() 

Diese session_scope können ohne Wiederholung der Kesselblech jetzt sauber verwendet werden.

class ThingOne(object): 
    def go(self, session): 
     session.query(FooBar).update({"x": 5}) 

class ThingTwo(object): 
    def go(self, session): 
     session.query(Widget).update({"q": 18}) 

def run_my_program(): 
    with session_scope() as session: 
     ThingOne().go(session) 
     ThingTwo().go(session) 
+10

Die SQLAlchemy-Entwickler dokumentieren eine mögliche, wahrscheinliche und einfache Implementierung, die das Lebensdauerproblem der Sitzung gut löst. Warum gingen sie nicht extra in die Meile und stellten sie als integrierte Funktion zur Verfügung, anstatt alle Bibliotheksbenutzer eine Version dieses Codes in ihre Codebasis umzuschreiben? – ereOn

+0

Guter Punkt, dachte auch das Gleiche. –

Verwandte Themen