Sqlalchemy

时间:2017-08-31 14:33:18

标签: python sqlalchemy mariadb

我有外部全局会话(通过Flask-SqlAlchemy),然后在一个函数中,我创建了另一个Session,它将一些数据提交到数据库(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 个答案:

答案 0 :(得分:0)

在global_session中将事务隔离级别更改为READ COMMITTED会有所帮助。

在上面的示例中,将global_session的定义更改为以下内容:

global_session = Session(
    bind=engine.execution_options(isolation_level='READ COMMITTED'))