我有外部全局会话(通过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')
答案 0 :(得分:0)
在global_session中将事务隔离级别更改为READ COMMITTED
会有所帮助。
在上面的示例中,将global_session的定义更改为以下内容:
global_session = Session(
bind=engine.execution_options(isolation_level='READ COMMITTED'))