web-dev-qa-db-ja.com

Djangoネストされたトランザクション-「with transaction.atomic()」-明確化を求める

Djangoのネストされたトランザクション-「with transaction.atomic()」 では、問題は次のとおりです...

_def functionA():
    with transaction.atomic():
        #save something
        functionB()

def functionB():
    with transaction.atomic():
        #save another thing
_

functionBが失敗してロールバックする場合、functionAもロールバックしますか?

ケビンクリストファーヘンリーは「はい、どちらかの関数で例外が発生した場合、両方ともロールバックされます。」と返信します。それから彼は the docs を引用し、次のように述べています:

アトミックブロックはネストできます。この場合、内側のブロックが正常に完了しても、後で外側のブロックで例外が発生した場合、その影響はロールバックされます。

このドキュメントの引用は、元の質問に対応していないようです。このドキュメントは、INNER BLOCK(functionB)が正常に完了したときに、OUTERブロック(functionA)が例外を発生させた場合でも、その効果をロールバックできると述べています。しかし、質問は反対のシナリオを指します。 INNERブロック(functionB)が失敗した場合、OUTERブロック(functionA)はロールバックされますか?このドキュメントの引用はそのシナリオを扱っていません。

ただし、ドキュメントのさらに下には、この例があります...

_from Django.db import IntegrityError, transaction

@transaction.atomic
def viewfunc(request):
    create_parent()

    try:
        with transaction.atomic():
            generate_relationships()
    except IntegrityError:
        handle_exception()

    add_children()
_

...この解説が続く...

この例では、generate_relationships()が整合性制約の違反によってデータベースエラーを引き起こした場合でも、add_children()でクエリを実行でき、create_parent()からの変更はそのまま残ります。

ドキュメントを正しく読んでいる場合は、generate_relationships()への呼び出し(元の質問のfunctionBへの呼び出しに類似)が失敗し、create_parent()およびadd_children()がデータベースにコミットされます。これは、ケビン・クリストファー・ヘンリーの答えと矛盾するようです。

私を困惑させているのは DjangoネストされたTransaction.atomic に同じ質問/回答が表示されることです。

Djangoとstackoverflowの両方が初めてなので、ドキュメントを読むことに自信はありませんが、これらの応答の両方に矛盾しているようです。より経験豊富な誰かからの説明のためにありがとうございました。

18
melodibit

ネストされたトランザクションブロックとデータベース操作XY、およびZを含むいくつかの疑似コードを次に示します。

with transaction.atomic():
    X
    with transaction.atomic():
        Y
    Z

Xが例外を発生させた場合、明らかに、どの操作も最初からコミットする機会を得られません。

Yが例外を発生させた場合(これはあなたが参照した質問でした)、外側のブロックもロールバックします。ネストされたトランザクション自体とは何の関係もありません。これは、Python例外がバブルアップするために発生します。外側のブロックは例外によって終了され、常にロールバックが発生します。それはそもそも例外の原因に関係なく真。

非自明なケースは、Zが例外を発生させたときに起こることであり、それがドキュメントが特別な注意のためにそれを呼び出している理由です。 参照 の場合、XYの両方がロールバックされます。

内側のブロックが正常に完了しても、後で外側のブロックで例外が発生した場合、その影響はロールバックされます。

これで、ネストされた操作によって発生した例外をキャッチすることもできます。

with transaction.atomic():
    X
    try:
        with transaction.atomic():
            Y
    except IntgrityError:
        pass
    Z

この場合、Yが例外をスローすると、内部ブロックは(例外で終了するため)ロールバックされますが、外部ブロックはロールバックしません(そうしないため)。

例外の捕捉を含まない特定の質問(コード例付き)に回答したため、あなたが言及した2つの回答のどちらの情報とも矛盾しません。

いずれにしても、フィードバックとより包括的な答えを与える機会に感謝します。

2番目の例はIntegrityErrorをキャッチするため、外部トランザクションアトミックは次のエラーの発生を認識しません

アトミックをtry/exceptブロックでラップすると、整合性エラーの自然な処理が可能になります

基本的には、次のブロックで外部トランザクションのロールバックを発生させない場合は、整合性エラーを試行/キャッチするだけです。

すでにドキュメントから次のように述べています

アトミックブロックはネストできます。この場合、内側のブロックが正常に完了すると、その効果はロールバックできます例外は後で外側のブロックで発生します

したがって、デフォルトでは、エラーが伝播するときにロールバックが発生します。2番目の例は、外部トランザクションのロールバックを停止する方法です。

1
iklinac