web-dev-qa-db-ja.com

sqlalchemyセッションでのオブジェクトの更新について

まあ、私はsqlalchemyとオブジェクトの更新についての疑問に対処しています!

2つのセッションがある状況にあり、両方のセッションで同じオブジェクトが照会されました!...特定の事柄のため、セッションの1つを閉じることができません。オブジェクトを変更してセッションAで変更をコミットしましたが、セッションBでは属性が初期属性です。変更なし!..

だから...変更を伝えるために何らかの通知システムを実装する必要がありますか、sqlalchemyでこれを行うための組み込みの方法がありますか?

24

セッションはこのように機能するように設計されています 。セッションBのオブジェクトの属性は、セッションBで最初に照会されたときの属性を保持します。さらに、SQLAlchemyは、他のセッションのオブジェクトが変更されたときに、それらを自動的に更新しようとはしません。このような。

各セッションの存続期間をデータベース内の単一のトランザクションとして積極的に考える必要があります。オブジェクトが古くなっている可能性があるという事実にセッションが対処する必要がある方法とタイミングは、SQLAlchemy(またはSQLAlchemyの拡張機能)に組み込まれているアルゴリズムで解決できる技術的な問題ではありません。自分で決めてコーディングしてください。 「正しい」応答は、これが問題ではないと言うことである可能性があります。セッションBで発生したロジックは、セッションBの開始時にデータを使用した場合に有効である可能性があります。あなたの「問題」は実際には問題ではないかもしれません。ドキュメントには実際には いつセッションを使用するかに関するセクション全体 がありますが、万能のソリューションを期待している場合はかなり厳しい応答が得られます...

セッションは通常、データベースアクセスが予想される論理操作の開始時に構築されます。

セッションは、データベースとの通信に使用されるときはいつでも、通信を開始するとすぐにデータベーストランザクションを開始します。自動コミットフラグが推奨されるデフォルトのFalseのままであると仮定すると、このトランザクションは、セッションがロールバック、コミット、またはクローズされるまで進行し続けます。セッションは、前のトランザクションが終了した後で再び使用される場合、新しいトランザクションを開始します。このことから、セッションは一度に1つだけですが、多くのトランザクションにわたって有効期間を持つことができます。これら2つの概念をトランザクションスコープとセッションスコープと呼びます。

ここでの含意は、SQLAlchemy ORMが、開発者が自分のアプリケーションでこれらの2つのスコープを確立することを奨励していることです。これには、スコープの開始時と終了時だけでなく、それらのスコープの拡張も含まれます。関数またはメソッド内の実行フローに対して、それはアプリケーション全体で使用されるグローバルオブジェクトであるか、これら2つの間のどこかである必要があります。

このスコープを決定するために開発者に課される負担は、SQLAlchemy ORMがデータベースの使用方法について必然的に強い意見を持つ1つの領域です。具体的には、作業ユニットのパターンは、時間の経過に伴う変更を累積し、定期的にフラッシュして、メモリ内の状態をローカルトランザクションに存在することがわかっているものと同期させることです。このパターンは、意味のあるトランザクションスコープが設定されている場合にのみ有効です。

とはいえ、状況のしくみを変えるためにできることがいくつかあります。

まず、セッションを開いたままにしておく時間を短縮できます。セッションBがオブジェクトを照会している場合、後でそのオブジェクトに対して(同じセッションで)属性を最新の状態にしたい何かを実行します。 1つの解決策は、この2番目の操作を別のセッションで実行することです。

もう1つの方法は、expire/refreshメソッドを使用することです docs show ...

_# immediately re-load attributes on obj1, obj2
session.refresh(obj1)
session.refresh(obj2)

# expire objects obj1, obj2, attributes will be reloaded
# on the next access:
session.expire(obj1)
session.expire(obj2)
_

session.refresh()を使用すると、セッションがすでにオブジェクトを以前にクエリした場合でも、オブジェクトの最新バージョンをすぐに取得できます。

43
Mark Hildreth

この問題が発生しましたが、既存のソリューションが何らかの理由で機能しませんでした。機能したのは、session.commit()を呼び出すことでした。それを呼び出した後、オブジェクトにはデータベースからの更新された値が含まれていました。

6
Nathan Wailes

これを実行して、選択したデータベースから最新の値をセッションに強制的に更新します。

session.expire_all()

Excellent DOCデフォルトの動作とセッションの寿命について

5
Tuanitim

TL; DRセッションの同期に取り組むのではなく、(複数の)セッションを使用せずに、タスクをSQLAlchemyコア構文を使用して、エンジン上で直接簡単にコーディングできるかどうかを確認します。

SQLとJDBCの経験から来た人にとって、SQLAlchemyについて学ぶための重要なことの1つは、残念ながら、数か月にわたって複数のドキュメントを読んだことはありませんでした。SQLAlchemyは、コアとORMの2つの根本的に異なる部分で構成されていることです。 。 ORMドキュメントが最初にWebサイトにリストされており、ほとんどの例がORMに似た構文を使用しているため、SQL/JDBCの観点からORMについて考える場合、ORMのドキュメントは、ORMに似た構文を使用しており、エラーや混乱に備えています。 ORMは独自の抽象化レイヤーを使用して、実際のSQLステートメントが実行される方法とタイミングを完全に制御します。経験則では、セッションの作成と削除は安価であり、再クエリ、同期、またはマルチスレッド化を引き起こす可能性のあるプログラムのフローおよびロジックのいかなるものにも再利用すべきではありません。一方、コアは、JDBCドライバーと非常によく似た、スリルのない直接的なSQLです。 docs に1か所あります。ORMではCoreを使用して「推奨」していることがわかりました。

ここでは、カウンターの増分やログテーブル内への追加行の挿入など、単純なSQL操作をConnection上で直接実行することをお勧めします。接続を処理するときは、コアレベルのSQL操作が使用されることが予想されます。例えばSQL式言語チュートリアルで説明されているもの。

ただし、Connectionを使用すると、Sessionを使用した場合と同じ副作用が発生するようです。特定のレコードを再クエリすると、DB内のレコードのコンテンツが変更された場合でも、最初のクエリと同じ結果が返されます。そのため、接続は「リアルタイム」でDBコンテンツを読み取るセッションと同じように「信頼性が低い」ようですが、直接エンジンを実行すると、プールから接続オブジェクトが選択されるため、正常に動作しているように見えます(取得された接続がクエリに対して、特定の開いている接続と同じ "再利用"状態)。 SA docsに従って、Resultオブジェクトを明示的に閉じる必要があります

0
SVUser