Я читал об использовании ORM SQLAlchemy в контексте приложения Twisted. Это много информации, чтобы переварить, так что у меня есть немного проблем с тем, чтобы собрать все части. До сих пор я собрал следующие абсолютные истины:Является ли это приемлемым способом выполнения запросов SQLAlchemy с резьбой от Twisted?
- Одна сессия подразумевает один поток. Всегда.
scoped_session
, по умолчанию, предоставляет нам способ ограничения сеансов для заданного потока. Другими словами, я уверен, что, используяscoped_session
, я не буду передавать сеансы другим потокам (если только я не сделаю это явно, чего я не буду).
Я также собрал, что есть некоторые проблемы, связанные с ленивой/нетерпеливой загрузкой, и что один из возможных подходов состоит в том, чтобы отделить объекты ORM от сеанса и повторно привязать их к другому сеансу при изменении потоков. Я довольно неясен в деталях, но я также пришел к выводу, что scoped_session
делает многие из этих моментов спорными.
Мой первым вопрос, является ли или не я сильно ошибся в своих выводах выше.
Помимо этого, я разработал этот подход, который, я надеюсь, удовлетворительный.
Я начинаю с создания scoped_session
объекта ...
Session = scoped_session(sessionmaker(bind=_my_engine))
... который я затем использовать с менеджером контекста для того, чтобы обрабатывать исключения и очистки изящно:
@contextmanager
def transaction_context():
session = Session()
try:
yield session
session.commit()
except:
session.rollback()
raise
finally:
session.remove() # dispose of the session
Теперь все, что мне нужно сделать, это использовать указанный выше менеджер контекста в функции, отложенной на отдельный поток. Я бросил вместе декоратора, чтобы сделать вещи немного симпатичнее:
def threaded(fn):
@wraps(fn) # functools.wraps
def wrapper(*args, **kwargs):
return deferToThread(fn, *args, **kwargs) # t.i.threads.deferToThread
return wrapper
Вот пример того, как я намерен использовать весь притон. Ниже приведена функция, которая выполняет поиск DB, используя SQLAlchemy ОРМ:
@threaded
def get_some_attributes(group):
with transaction_context() as session:
return session.query(Attribute).filter(Attribute.group == group)
Мой второй вопрос, является ли или нет этот подход является жизнеспособным.
- Я делаю какие-либо принципиально ошибочные предположения?
- Есть ли какие-либо оговорки?
- Есть ли лучший способ?
Edit:Here является родственным вопросом о неожиданной ошибке в моем контексте менеджере.
Ну, я думаю, главный вопрос, который у всех будет: работает ли так, как вы его закодировали? – bitcycle
@bitcycle, кстати (и удивительно), нет ... это не работает. Я получаю 'AttributeError' в моем диспетчере контекстов - видимо,' Session' не имеет атрибута 'remove'.Это довольно удивительно - – blz
Это выглядит в основном правильно (за исключением того, что это должно быть 'Session.remove()'). Следует иметь в виду, что некоторые драйверы базы данных, такие как 'sqlite3', не позволяют передавать их потоки между потоками. Я уверен, что объекты 'scoped_session' обрабатывают это соответствующим образом (прошло некоторое время с тех пор, как я использовал SQLAlchemy). –