6

私のDjangoアプリケーションは、次のようなビュー内で実行しているカスタムSQLを使用しています:

db = router.db_for_write(model)
cursor = connections[db].cursor()
cursor.execute("INSERT INTO ....")

を使用しているTransactionMiddlewareため、ビューはトランザクション内で実行されていますが、このような新しいカーソルを取得して現在開いているトランザクションを「エスケープ」するのか、それともカーソルがまだ開いているトランザクションの一部なのかはわかりません。カーソルがトランザクション内で実行されていると思わせるエラー メッセージが表示されます。

によって開かれたトランザクションの外部で、カーソルを使用して SQL コマンドを実行できるようにしたいと考えていますTransactionMiddleware。これは可能ですか?

問題があれば、PostgreSQL 8.4 データベースで Django 1.4 を実行しています。

4

2 に答える 2

2

トランザクションを手動で管理したいビューがある場合は、そのビューのデコレーターを使用して commit_manually する必要があります。

ドキュメントから。

from django.db import transaction

@transaction.commit_manually
def viewfunc(request):
    ...
    # You can commit/rollback however and whenever you want
    transaction.commit()
    ...

    # But you've got to remember to do it yourself!
    try:
        ...
    except:
        transaction.rollback()
    else:
        transaction.commit()

@transaction.commit_manually(using="my_other_database")
def viewfunc2(request):
    ....

はい、トランザクション カーソルをインポートすると、トランザクションのカーソルが提供されるだけで、新しいトランザクションは作成されません。

于 2012-07-19T06:53:21.627 に答える
2

別の同時トランザクションを取得するには、別のデータベース接続が必要になると思います。また、django はデータベースごとに 1 つの接続しか管理しないと確信しています。しかし、別のものを作成することはできます。これをしない正当な理由があるかもしれません。コンプレックスが頭に浮かびます。

私はこのようなものがうまくいくと思います:

from django.conf import settings
from django.db.utils import ConnectionHandler

def my_view(request):
    """Flirt with complexity by using two connections to db"""
    private_connections = ConnectionHandler(settings.DATABASES)
    db = router.db_for_write(model)
    new_conn = private_connections[db]
    new_conn.enter_transaction_management()
    new_conn.managed(True)
    new_cur = new_conn.cursor()
    new_cur.execute("INSERT INTO ...")
    new_conn.commit()
    new_conn.close()

django.db.transactionのグローバル接続インスタンスで動作するため使用できないことに注意してくださいdjango.db.connections

本当の問題は、なぜこれをやりたいのかということだと思います! そして、ラクシュマン・プラサドの答えのどこが間違っているのでしょうか? いつでもコミット/ロールバックできるため、単一のビュー内の異なるトランザクションで異なるタスクを実行することを妨げるものは何もありません。トランザクションが並列でなければならず、連続してはならないという事実は、それらの間の何らかの論理的なつながりを暗示しています。私の考えでは、トランザクションが実際には同じトランザクションにある必要があることを示しています。

一方、なんらかのオフライン処理をエミュレートしようとしているだけで、その成功または失敗がビューに特に関係しない場合は、メッセージ キューを設定し、これらの挿入を個別に実行することを検討してください。処理する。 Celeryはまさにそれを行うための人気のあるパッケージです。ただし、応答時間が大きな問題ではない場合でも、連続したトランザクションで十分だと思います。

アップデート:

ビジネス ロジックを 1 つの (個別の) トランザクションで実行しながら、データベースにバックアップされたキャッシュを自動コミット モードで動作させたい場合は、django の方法があります。あなたがする必要があるのは、キャッシングが外部で発生することを確認することだけですcommit_on_success:

  • キャッシング ミドルウェアを使用しているだけの場合は、TransactionMiddleware.

  • TransactionMiddleware キャッシング ビュー デコレーターを使用する場合は、無効にする (または問題のビューをデコレーター内に配置する) ことができ、キャッシングデコレーターautocommitでデコレーターを使用できると思います。おかしいように見えますが、なぜうまくいかないのかわかりません:commit_on_success

    @transaction.autocommit
    @cache_page(500)
    @transaction.commit_on_success
    def my_view(request):
        "..."
    
  • テンプレート キャッシングを使用するか、より複雑な手動キャッシングを行う場合は、無効にするTransactionMiddleware(または問題のあるビューをautocommitデコレータ内に配置する) こともできます。またcommit_on_success、コンテキスト マネージャーとして使用して、必要なコードのみをマネージド トランザクションに配置し、残りのビューを残すこともできます。自動コミットで。

    @transaction.autocommit
    def my_view(request):
        data = cache.get(some_key)
        with transaction.commit_on_success():
            context = do_some_processing(data)
        cache.set(some_key, context['data'])
        return render('template/with/cache/blocks.html', context=context)
    
于 2012-09-11T14:42:56.150 に答える