0

きちんと整理するために、これを別の質問にすると思いました。これは、SQLAlchemyがデータベースとSQLAlchemyセッションを更新しないことに基づいて います:データベースを存続させる方法は?

これが取引です。デーモンと通信するPyramidアプリケーションがあり、デーモンはデータベースと通信します。

次のように、データベースセッション変数にデータベースを追加すると、何らかの理由でデータベースにコミットされません。

DBSession.add(ModelInstance)

フラッシュまたはコミットを呼び出しても、コミットされません。

これが私がDBSessionを作る方法です:

    settings = {
        'sqlalchemy.url':'blah blah'
        }
    DBSession = scoped_session(sessionmaker(extension=ZopeTransactionExtension()))
    engine = engine_from_config(settings, 'sqlalchemy.')
    DBSession.configure(bind=engine)

データベースを正常にクエリできるので、これは私には問題ないようです。すなわち:この種のものは機能します:

DBSession.query(ModelClass).get(id)

この立派な紳士https://stackoverflow.com/users/100297/martijn-pietersは、次の少しのコードの使用を提案しました。

import transaction
transaction.commit()

そして、それは私のものがコミットされたことを確認するためにうまくいきました。唯一の問題は、それがどういうわけか私のDBSessionを役に立たなくすることです。したがって、セッションが追跡しているオブジェクトを使用する場合は、セッションとそれらのアイテムを再インスタンス化する必要があります。これは最悪だ。かなりの時間がかかります。

私の質問は、要するに、どうすればこれを回避できるかということです。

そして長い間:

  • DBSessionを中断せずに正しくコミットするにはどうすればよいですか?

また

  • 長時間のデータベース呼び出しを必要とせずに、DBSessionと関連するモデルインスタンスを修正するにはどうすればよいですか?

  • なぜこれが起こっているのか考えていますか?私が言及したPyramidアプリ内で同じ方法でDBSessionを正常に構築しましたが、それは完全に正常に機能し、必要なときにすべてをコミットしました。

私が遭遇したエラーの詳細については、最初に述べた2つの質問を参照してください。

4

3 に答える 3

2

sqlalchemyのドキュメントによると

のもう1つの動作commit()は、デフォルトでは、コミットの完了後に存在するすべてのインスタンスの状態が期限切れになることです。これは、属性アクセスを介して、またはQuery結果セットに存在することによって、インスタンスが次にアクセスされるときに、最新の状態を受け取るようにするためです。この動作を無効にするには、で構成sessionmaker()expire_on_commit=Falseます。

問題のように見えることは、実際には意図的に行われています。コミットすると、すべてのオブジェクトが期限切れとしてマークされます。これは、データベースで変更された可能性のある古いキャッシュ値を使用し続けないようにするために行われます。

ピラミッドで機能する理由は、各リクエストに独自のトランザクションがあり、それらを処理する前にオブジェクトをクエリするためです。プレビュートランザクションのオブジェクトを使用しようとしましたが、データベースと同期していない可能性があるため、これはお勧めできません。

問題を解決するには、トランザクションの終了後にオブジェクトを再利用しないようにするか(トランザクションにさらに多くのものを含める必要がある場合があります)、expire_on_commit=Falseトランザクションのアドバイスに従って使用できます。ただし、後者を使用する場合は、オブジェクトが古くなっている可能性があることに注意してください。

于 2012-10-25T11:05:04.303 に答える
2

SQLAlchemyセッションでは、コミット時に管理しているオブジェクトの有効期限が切れます。コミット後、他の何かがデータベースにミラーリングしようとしている状態を変更していないという保証が並行世界にないため、これは正気です。

Pyramidは、リクエストごとに1つのトランザクションを維持するのに役立つトランザクションマネージャーの使用をお勧めします。transaction.commit()リクエストが完了すると、自動的に呼び出されます。このように、オブジェクトの期限切れについて考える/心配する必要はなく、コードで例外が発生した場合、トランザクションは適切に中止されます。

トランザクションマネージャを設定する方法は、をインストールpyramid_tmしてzope.sqlalchemyからに接続するDBSessionことzope.sqlalchemy.ZopeTransactionExtensionです。そうすれば、物事は「うまくいく」でしょう。

DBSession = scoped_session(sessionmaker(extension=ZopeTransactionExtension()))

# ...

def main(global_conf, **settings):
    config = Configurator(...)
    config.include('pyramid_tm')
    # ...

新しいオブジェクトの主キーを設定する必要がある場合、または一部のSQLが正しく実行されることを確認する必要がある場合はDBSession.flush()、実際にコミットせずにトランザクション内でSQLを実行するために使用できます。エラーが発生した場合は、キャッチして対処することができます。

セッションのこの基本的なセットアップは、Pyramidドキュメントのチュートリアルで説明されています。

http://docs.pylonsproject.org/projects/pyramid/en/1.4-branch/tutorials/wiki2/basiclayout.html

更新:Pyramidでトランザクションマネージャーを使用することについてのあなたの質問に答えたのだと気づきました。これはすでに正常に使用されています。答えは、で何が起こっているのかを明確に説明していると思いますが、ZopeTransactionExtensionトランザクションのコミットについて確認する必要があります。スクリプトで1つのトランザクションを使用するのが賢明です。これは、次の方法で作成できます。

import transaction

with transaction.manager:
    # do tons of database stuff

これで、例外が発生した場合はトランザクションが中止され、発生しなかった場合はコミットされます。

于 2012-10-25T15:00:46.883 に答える
0

私がこれを正しく読んでいれば、私は何が起こっているのか知っていると思います...

ピラミッド/ゾープトランザクションマネージャーの欠点は、それらがすべてまたはまったくないことです。実装方法が原因で、それらを使用してcommit()を呼び出すことはできません。正確な理由は覚えていませんが、これと何時間も戦った後、一度すべてのコードを掘り下げました。ページ内でコミットする方法がありませんでした。

さまざまな理由から、アプリで自動トランザクションラッピングを使用しないことにしました。1つ以上のコミットが必要なシナリオや、セーブポイントを使用する必要があるシナリオがたくさんありました(postgresqlを使用しています)。

于 2012-10-26T00:59:16.703 に答える