У меня есть приложение на Python с несколькими небольшими функциями взаимодействия с базой данных, которые используют sqlalchemy.Примером небольшого взаимодействия является foo()
, которое извлекает значение bar_id
из таблицы Bar или добавляет значение по имени, генерирует идентификатор и возвращает новый идентификатор, если bar_name
не найдено.У меня также есть более крупные взаимодействия с базой данных (пример: big_func()
), которые объединяют меньшие взаимодействия, такие как foo()
:
@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()
def foo(bar_name, session):
"""
Look up bar by name and return bar_id.
If bar doesn't exist in database, add it and get an id.
"""
db_bar = session.query(Bar) \
.filter_by(bar_name=bar_name) \
.first()
if db_bar is None:
db_bar = Bar(bar_name=bar_name)
session.add(db_bar)
session.flush()
return db_bar.bar_id
def big_func(df):
with session_scope() as session:
widg_id = foo("widget", session)
# 1. calculations and other changes
# 2. add and commit additional data to database
Мой вопрос касается управления сеансами.Я хотел бы выполнить все следующие условия:
foo()
можно вызвать напрямую, не передавая ему сеанс.В этом случае он создает сеанс, фиксирует изменения и закрывает сеанс. - Когда вызывается
big_func
, все изменения в базе данных либо фиксируются, либо откатываются вместе.То есть foo()
совместно использует сеанс с big_func()
и не вызывает session.commit()
. - Дополнительные меньшие взаимодействия
foo_2()
, foo_3()
и т. Д. Могут вести себя аналогично, не копируя биопланшет.
В приведенном выше коде условие 1 не выполняется.Мое решение сделать foo()
вызываемым независимо требует стека шаблонов и не использует контекстный менеджер:
def foo(bar_name, session=None):
if session is None:
session = Session()
session_is_local = True
else:
session_is_local = False
db_bar = session.query(Bar) \
.filter_by(bar_name=bar_name) \
.first()
if db_bar is None:
db_bar = Bar(bar_name=bar_name)
session.add(db_bar)
session.flush()
if session_is_local:
session.commit()
session.close()
return db_bar.bar_id
Есть ли лучший или более идиоматический способ сделать это?