2017-08-31 52 views
0

外部のグローバルセッション(Flask-SqlAlchemy経由)があり、関数内でデータをデータベースにコミットする別のセッション(mariadbバックエンド)を作成します。ただし、データは閉じられるまで外部セッションからアクセスできません。Sqlalchemyの2つのネストされたセッション

例:

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)と同じトランザクション内にあるため、内部セッションにコミットされたデータは、外部のセッションにアクセス可能になるように内部のセッションを作成することができますか?

アップデート:ここで

が最小完全かつ検証可能な例であり、それはより良い問題を説明したいと考えています。 Flask/flask-sqlalchemyは必要ありません。

役立ちますglobal_sessionに READ COMMITTEDにトランザクション分離レベルを変更する
import sqlalchemy as sa 

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


engine = sa.create_engine('mysql+mysqldb://user:[email protected]/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

私が正しくあなたを理解している場合、これはまた、セーブポイントとして知られているサブトランザクションのための仕事、のように思えます。 SQLAlchemyでは、['Session.begin_nested()'](http://docs.sqlalchemy.org/en/latest/orm/session_api.html#sqlalchemy.orm.session.Session.begin_nested)でセーブポイントを開始します。新しいトランザクションが開始するまで「外部」セッションがコミットされたデータを参照しない理由は、MySQLの場合と同じです(MariaDBのデフォルトのトランザクション分離レベルは繰り返し可能です)。(https://mariadb.com/kb/en/ -mariadb-library/set-transaction /#isolation-levels)を使用します。 –

+0

「外部セッション経由の挿入」の記述はbtwです。本当に曖昧。あなたが持っている問題を記述する[mcve]を試してみてください(その時点でコミットすることはできません)。 –

+0

サブトランザクションを使用したくない場合は、アーキテクチャを過度に複雑にすることをお勧めします。再考する。 –

答えて

0

。上記の例では

、次のようにglobal_sessionの変更定義:

global_session = Session(
    bind=engine.execution_options(isolation_level='READ COMMITTED')) 
関連する問題