26

sqlalchemy を使用して、多数の小さなデータベース アクセス関数を備えた Python アプリケーションがあります。私は、これらの関数の周りに多くの定型的なセッション処理コードを持たないようにしています。

次のような関数が多数あります。

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()

これらの関数をリファクタリングしようとしていますが、まだ最善のアプローチがあるかどうかはわかりません。私が現在持っている最高のものは次のとおりです。

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)

これを行うためのより良い、またはより慣用的な方法はありますか? おそらくデコレータを使用していますか?

ありがとう、ジョン

4

4 に答える 4

46

SQLAlchemy のドキュメントでは、コンテキスト マネージャーを使用してこれを行う方法が示されています。

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-閉じる

完全を期すために、ここにコード スニペットをコピーします。

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()

これsession_scopeでボイラープレートを繰り返さずにきれいに使えます。

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)
于 2015-04-22T17:49:33.050 に答える
0

コンテキスト マネージャを使用するという morphyn の提案は適切です。contextlib.contextmanager最初の と非常によく似た関数にデコレータを適用し、 get_ticket_historytry と except の間のコードをyieldステートメントに置き換え、名前を変更することで、このようなコンテキスト マネージャーを作成できますtransactionPEP 343には、その名前のほぼ同一の例があります。

次に、そのコンテキスト マネージャーを with ステートメントで使用して再実装しget_ticket_historyます。ただし、SQLAlchemy は既にメソッドとしてその関数を提供しているようですbegin

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

于 2013-02-10T19:17:20.300 に答える