web-dev-qa-db-ja.com

Flask-SQLAlchemy-セッションは複数のデータベースでどのように機能しますか?

私はFlaskプロジェクトに取り組んでおり、Flask-SQLAlchemyを使用しています。
複数の既存のデータベースを操作する必要があります。
「app」オブジェクトとSQLAlchemyオブジェクトを作成しました。

_from flask import Flask
from flask_sqlalchemy import SQLAlchemy


app = Flask(__name__)
db = SQLAlchemy(app)
_

構成では、デフォルトの接続と追加のバインドを設定しました。

_SQLALCHEMY_DATABASE_URI = 'postgresql://pg_user:pg_pwd@pg_server/pg_db'
SQLALCHEMY_BINDS = {
    'Oracle_bind': 'Oracle://Oracle_user:Oracle_pwd@Oracle_server/Oracle_schema',
    'mssql_bind': 'mssql+pyodbc://msssql_user:mssql_pwd@mssql_server/mssql_schema?driver=FreeTDS'
}
_

次に、宣言型システムを使用してテーブルモデルを作成し、必要に応じて___bind_key___パラメーターを設定して、テーブルがどのデータベースにあるかを示します。
例えば:

_class MyTable(db.Model):
    __bind_key__ = 'mssql_bind'
    __tablename__ = 'my_table'

    id = db.Column(db.Integer, nullable=False, primary_key=True)
    val = db.Column(db.String(50), nullable=False)
_

このようにして、すべてが正しく機能します。クエリを実行すると、適切なデータベースでクエリが実行されます。

SQLAlchemyドキュメントとFlask-SQLALchemyドキュメントを読む私はこれらのことを理解しています(正しく理解していることを確認するためにそれらを書き留めます):

  • セッションを通じてトランザクションを処理できます。
  • SQLAlchemyでは、セッションを特定のエンジンにバインドできます。
  • Flask-SQLAlchemyは、リクエストの開始時にセッション(scoped_session)を自動的に作成し、リクエストの終了時にそれを破棄します

だから私はできる:

_record = MyTable(1, 'some text')
db.session.add(record)
db.session.commit()
_

Flask-SqlAlchemyで、セッションに関して複数のデータベースを使用するとどうなるか理解できません。

システムが___bind_key___パラメーターを介して適切なデータベースでテーブルを正しくバインドできることを確認しました。したがって、_db.session_を介して異なるデータベースにデータを挿入でき、コミット時にすべてが保存されます。 。

ただし、Flask-SQLAlchemyが複数のセッション(エンジンごとに1つ)を作成するのか、それとも別の方法で管理するのかはわかりません。
どちらの場合も、特定のデータベースのセッション/トランザクションを参照するにはどうすればよいですか?
db.session.commit()を使用すると、システムは関連するすべてのデータベースでコミットを実行しますが、単一のデータベースに対してのみコミットする場合はどうすればよいですか?
私は次のようなことをします:

_db.session('mssql_bind').commit()
_

しかし、私はこれを行う方法を理解することはできません。

これらの状況の管理を容易にするFlask-SQLAlchemyの実装も見ました。

問題: https://github.com/mitsuhiko/flask-sqlalchemy/issues/107
実装: https://github.com/mitsuhiko/flask-sqlalchemy/pull/249

しかし、使い方がわかりません。

Flask-SQLAlchemyで、単一のエンジンごとにセッションを管理するにはどうすればよいですか?

12
corros

Flask-SQLAlchemyは カスタマイズされたセッション を使用し、指定された マップされたクラスの___bind_key___属性 に従ってバインドルーティングを処理します。内部では、実際にそのキーを情報として作成されたテーブルに追加します。言い換えると、Flaskは、バインドごとに1つずつ、複数のセッションを作成しませんが、バインドキーに従って正しい接続可能(エンジン/接続)にルーティングする単一のセッションを作成します。VanillaSQLAlchemyには 同様の機能 箱から出して。

どちらの場合も、特定のデータベースのセッション/トランザクションをどのように参照できますか? db.session.commit()を使用すると、システムは関連するすべてのデータベースでコミットを実行しますが、単一のデータベースに対してのみコミットする場合はどうすればよいですか?

セッションが所有する接続を使用して、セッションの途中で特定のデータベースを破壊してコミットを発行することはお勧めできません。セッションは全体であり、 状態を追跡します オブジェクトインスタンスの場合、必要に応じてデータベースへの変更をフラッシュします。つまり、セッションによって処理されるトランザクションは、データベーストランザクションだけでなく、セッション自体のトランザクションでもあります。同様に。すべてが1つとしてコミットおよびロールバックする必要があります。

一方、新しいSQLAlchemy(またはFlask-SQLAlchemy)セッションを作成して、おそらく バインドの1つで進行中のトランザクションに参加する

_session = db.create_scoped_session(
    options=dict(bind=db.get_engine(app, 'Oracle_bind'),
                 binds={}))
_

これがプルリクエストの目的です。これにより、既存のトランザクション接続をtheバインドとして新しいFlask-SQLAlchemyセッションに使用できます。これは、プルリクエストの論理的根拠に見られるように、たとえばテストで非常に役立ちます。そうすれば、たとえばテストで行われたすべてをロールバックできる「マスター」トランザクションを持つことができます。

bind_keyが存在する場合、SignallingSessionは常にdb.get_engine()メソッドを参照することに注意してください。これは、サンプルセッションがバインドキーなしでテーブルをクエリできず、Oracle DBに存在しないが、mssql_bindキーを持つテーブルでは引き続き機能することを意味します。

一方、リンクした問題には、特定のバインドにSQLを発行する方法がリストされています。

_rows = db.session.execute(query, params,
                          bind=db.get_engine(app, 'Oracle_bind'))
_

他にも明示的でないメソッドがリストされていますが、明示的は暗黙的よりも優れています。

17
Ilja Everilä