6

この簡単な例を考えてみましょう:

# a bank account class
class Account:
    @transaction.commit_on_success
    def withdraw(self, amount):
        # code to withdraw money from the account 

    @transaction.commit_on_success
    def add(self, amount):
        # code to add money to the account

# somewhere else
@transaction.commit_on_success
def makeMoneyTransaction(src_account, dst_account, amount):
    src_account.withdraw(amount)
    dst_account.add(amount)

( https://code.djangoproject.com/ticket/2227から取得)

で例外が発生した場合、 Django は現在ネストされたトランザクションを処理しないためAccount.add()、 のトランザクションAccount.withdraw()は引き続きコミットされ、お金が失われます。

Django にパッチを適用せずに、コミットがデータベースに送信されるようにするにはどうすればよい@transaction.commit_on_successでしょうか。ただし、例外を発生させずにデコレータの下のメイン関数が終了した場合のみです。

私はこのスニペットに出くわしました: http://djangosnippets.org/snippets/1343/そしてそれは仕事をすることができるようです. 使用する上で注意すべきデメリットはありますか?

ご協力いただける場合は、事前に多大な感謝を申し上げます。

PS可視性のために、以前に引用したコードスニペットをコピーしています。

def nested_commit_on_success(func):
    """Like commit_on_success, but doesn't commit existing transactions.

    This decorator is used to run a function within the scope of a 
    database transaction, committing the transaction on success and
    rolling it back if an exception occurs.

    Unlike the standard transaction.commit_on_success decorator, this
    version first checks whether a transaction is already active.  If so
    then it doesn't perform any commits or rollbacks, leaving that up to
    whoever is managing the active transaction.
    """
    commit_on_success = transaction.commit_on_success(func)
    def _nested_commit_on_success(*args, **kwds):
        if transaction.is_managed():
            return func(*args,**kwds)
        else:
            return commit_on_success(*args,**kwds)
    return transaction.wraps(func)(_nested_commit_on_success)
4

2 に答える 2

5

このスニペットの問題は、外側のトランザクションをロールバックせずに内側のトランザクションをロールバックできないことです。例えば:

@nested_commit_on_success
def inner():
    # [do stuff in the DB]

@nested_commit_on_success
def outer():
    # [do stuff in the DB]
    try:
        inner()
    except:
        # this did not work, but we want to handle the error and
        # do something else instead:

        # [do stuff in the DB]

outer()

上記の例でinner()は、例外が発生しても、その内容はロールバックされません。

必要なのは、内部の「トランザクション」のセーブポイントです。コードの場合、次のようになります。

# a bank account class
class Account:
    def withdraw(self, amount):
        sid = transaction.savepoint()
        try:
            # code to withdraw money from the account
        except:
            transaction.savepoint_rollback(sid)
            raise

    def add(self, amount):
        sid = transaction.savepoint()
        try:
            # code to add money to the account
        except:
            transaction.savepoint_rollback(sid)
            raise

# somewhere else
@transaction.commit_on_success
def makeMoneyTransaction(src_account, dst_account, amount):
    src_account.withdraw(amount)
    dst_account.add(amount)

Django 1.6 の時点で、atomic()デコレータはまさにそれを行います: デコレータの外部使用にはトランザクションを使用し、内部使用にはセーブポイントを使用します。

于 2013-02-12T21:18:46.567 に答える
2

Django 1.6で@atomicが導入されました。これはまさに私が探していたものです!

「入れ子になった」トランザクションをサポートするだけでなく、古くてあまり強力でないデコレータを置き換えます。また、さまざまな Django アプリ間でトランザクションを管理するための独自の一貫した動作を持つことは良いことです。

于 2013-09-04T18:15:28.503 に答える