Два вложенных сеанса в Sqlalchemy

У меня есть внешний глобальный сеанс (через Flask-SqlAlchemy), а затем внутри функции я создаю другой сеанс, который фиксирует некоторые данные в базе данных (бэкэнд mariadb). Однако данные недоступны из внешнего сеанса, пока они не будут закрыты.

Пример:

db = SqlAlchemy()

def func():
    s = db.Session()

    with s.no_autoflush:
        obj = models.MyObj(var="test")
        s.add(obj)
        # This inner session is needed because we can't do commits
        # in this session at this point, but still do some inserts
        # via outer session (db.session).

    # Finally we commit inner session to database.
    s.commit()

    # This assertion will fail because data is not accessible
    # in outer session.

    # db.session.close() here would help, but it is not desirable

    assert db.session.query(MyObj).filter_by(var="test").first()
    # -> this fails.

Как я могу создать внутренний сеанс так, чтобы он был в той же транзакции, что и внешний сеанс (db.session), и, таким образом, данные, зафиксированные во внутреннем сеансе, были бы доступны во внешнем сеансе?

Обновить:

Вот минимально полный и проверяемый пример, надеюсь, он лучше объясняет проблему. Колба / колба-sqlalchemy не нужна.

import sqlalchemy as sa

from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker


engine = sa.create_engine('mysql+mysqldb://user:password@mariadb/mydatabase')
#engine = sa.create_engine('sqlite:///:memory:')
Session = sessionmaker(bind=engine)
global_session = Session()

Model = declarative_base()


class MyTable(Model):
    __tablename__ = 'mytable'
    id = sa.Column(sa.Integer, primary_key=True)
    var = sa.Column(sa.String(length=255), unique=True, nullable=False)


def func():
    internal_session = Session()

    with internal_session.no_autoflush:
        # We add objects to internal_session, but we can't flush them yet (their linkage etc.
        # will be built gradually here)

        obj = MyTable(var="test")
        internal_session.add(obj)

        # At the same time we add some objects via global_session
        obj2 = MyTable(var='test2')

        global_session.add(obj2)
        global_session.commit()

        # If we perform any select query to global_session here, we will face problems later (at [*]).
        # If we comment out this line [*] is fine.
        assert not global_session.query(MyTable).filter_by(var='whatever!').first()

    # Finally we commit inner session to database.
    internal_session.commit()

    # This assertion will fail because data is not accessible
    # in outer session.

    # global_session.close() # here would help, but it is not desirable

    # [*]: this assertion will fail.
    assert global_session.query(MyTable).filter_by(var='test').first()

if __name__ == '__main__':
    try:
        Model.metadata.drop_all(engine)
    except:
        pass

    Model.metadata.create_all(engine)

    func()
    print('Ready')

1 ответ

Решение

Изменение уровня изоляции транзакции на READ COMMITTED в global_session помогает.

В приведенном выше примере измените определение global_session на следующее:

global_session = Session(
    bind=engine.execution_options(isolation_level='READ COMMITTED'))
Другие вопросы по тегам