Подтвердить что ты не робот

Избегание кода обработки сеанса шаблона в функциях sqlalchemy

У меня есть приложение python, которое имеет множество небольших функций доступа к базе данных, используя sqlalchemy. Я стараюсь избегать большого количества кода обработки сессий с помощью шаблонов вокруг этих функций.

У меня есть несколько функций, которые выглядят примерно так:

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)

Есть ли лучший или более идиоматический способ сделать это? Возможно, используя декоратор?

Спасибо, Джон

4b9b3361

Ответ 1

Документация 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-close-it

Копирование фрагмента кода здесь для полноты:

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)

Ответ 2

Предложение морфина использовать контекстный менеджер - это хорошо. Вы можете создать такой менеджер контекста, применив декоратор contextlib.contextmanager к функции, очень похожей на ваш первый get_ticket_history, заменив код между try и за исключением оператора yield и переименовав его, скажем, transaction. PEP 343 имеет почти идентичный пример этого имени.

Затем используйте этот менеджер контекста с оператором with для переопределения get_ticket_history. Похоже, что SQLAlchemy уже предоставляет эту функцию, как метод begin:

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