W3Cschool
恭喜您成為首批注冊用戶
獲得88經(jīng)驗值獎勵
有時你需要執(zhí)行與當(dāng)前數(shù)據(jù)庫事務(wù)相關(guān)的操作,但前提是事務(wù)成功提交。
Django 提供了 on_commit() 函數(shù)來注冊在事務(wù)成功提交后應(yīng)該執(zhí)行的回調(diào)函數(shù):
將任意函數(shù)(無參數(shù))傳遞給 ?on_commit()
?:
from django.db import transaction
def do_something():
pass # send a mail, invalidate a cache, fire off a Celery task, etc.
transaction.on_commit(do_something)
你也可以使用 ?lambda
?包裝函數(shù):
transaction.on_commit(lambda: some_celery_task.delay('arg1'))
傳入的函數(shù)將在成功提交調(diào)用“?on_commit()
?”的假設(shè)數(shù)據(jù)庫寫操作后立即被調(diào)用。
無任何活動事務(wù)時調(diào)用 ?on_commit()
? ,則回調(diào)函數(shù)會立即執(zhí)行。
如果假設(shè)的數(shù)據(jù)庫寫入被回滾(尤其是在 ?atomic()
? 塊里引發(fā)了一個未處理異常),函數(shù)將被丟棄且永遠不會被調(diào)用。
正確處理保存點(即嵌套了 ?atomic()
? 塊)。也就是說,注冊在保存點后的 ?on_commit()
? 的調(diào)用(嵌套在 ?atomic()
? 塊)將在外部事務(wù)被提交之后調(diào)用,但如果在事務(wù)期間回滾到保存點或任何之前的保存點之前,則不會調(diào)用:
with transaction.atomic(): # Outer atomic, start a new transaction
transaction.on_commit(foo)
with transaction.atomic(): # Inner atomic block, create a savepoint
transaction.on_commit(bar)
# foo() and then bar() will be called when leaving the outermost block
另一方面,當(dāng)保存點回滾時(因引發(fā)異常),內(nèi)部調(diào)用不會被調(diào)用:
with transaction.atomic(): # Outer atomic, start a new transaction
transaction.on_commit(foo)
try:
with transaction.atomic(): # Inner atomic block, create a savepoint
transaction.on_commit(bar)
raise SomeError() # Raising an exception - abort the savepoint
except SomeError:
pass
# foo() will be called, but not bar()
事務(wù)提交后的的回調(diào)函數(shù)執(zhí)行順序與當(dāng)初注冊時的順序一致。
如果一個帶有給定事務(wù)的 ?on-commit
? 函數(shù)引發(fā)了未捕獲的異常,那么同一個事務(wù)里的后續(xù)注冊函數(shù)不會被運行。這與你在沒有 ?on_commit()
? 的情況下順序執(zhí)行函數(shù)的行為是一樣的。
你的回調(diào)會在成功提交之后執(zhí)行,因此回調(diào)里的錯誤引發(fā)事務(wù)回滾。它們在事務(wù)成功時有條件的執(zhí)行,但它們不是事務(wù)的一部分。對于有預(yù)期的用例(郵件提醒,Celery 任務(wù)等),這樣應(yīng)該沒啥問題。如果它不是這樣的用例(如果你的后續(xù)操作很關(guān)鍵,以至于它的錯誤意味著事務(wù)失?。?,那么你可能不需要使用 ?on_commit()
? 鉤子。相反,你可能需要兩階段提交——比如兩階段提交協(xié)議支持( psycopg Two-Phase Commit protocol support )和在 Python DB-API 里說明的可選兩階段提交擴展( optional Two-Phase Commit Extensions in the Python DB-API specification ) 。
直到在提交后的連接上恢復(fù)自動提交,調(diào)用才會運行。(因為否則在回調(diào)中完成的任何查詢都會打開一個隱式事務(wù),防止連接返回自動提交模式)
當(dāng)在自動提交模式并且在 ?atomic()
? 塊外時,函數(shù)會立即自動運行,而不會提交。
?on-commit
? 函數(shù)僅適用于自動提交模式( ?autocommit mode
? ),并且 ?atomic()
? (或 ?ATOMIC_REQUESTS
? )事務(wù)API。當(dāng)禁用自動提交并且當(dāng)前不在原子塊中時,調(diào)用 ?on_commit()
? 將導(dǎo)致錯誤。
Django 的 ?TestCase
類將每個測試包裝在一個事務(wù)中,并在每次測試后回滾該事務(wù),以提供測試隔離。 這意味著實際上沒有任何事務(wù)被提交,因此您的 ?on_commit()
? 回調(diào)將永遠不會運行。
您可以通過使用 ?TestCase.captureOnCommitCallbacks()
? 來克服這個限制。 這會在列表中捕獲您的 ?on_commit()
? 回調(diào),允許您對它們進行斷言,或通過調(diào)用它們來模擬事務(wù)提交。
克服限制的另一種方法是使用 ?TransactionTestCase
? 而不是 ?TestCase
?。 這意味著您的事務(wù)已提交,并且回調(diào)將運行。 但是 ?TransactionTestCase
在測試之間刷新數(shù)據(jù)庫,這比 ?TestCase
的隔離要慢得多。
事務(wù)回滾鉤子相比事務(wù)提交鉤子更難實現(xiàn),因為各種各樣的情況都可能造成隱式回滾。
比如,如果數(shù)據(jù)庫連接被刪除,因為進程被殺而沒有機會正常關(guān)閉,回滾鉤子將不會運行。
解決方法是:與其在執(zhí)行事務(wù)時(原子操作)進行某項操作,當(dāng)事務(wù)執(zhí)行失敗后再取消這項操作,不如使用 ?on_commit()
? 來延遲該項操作,直到事務(wù)成功后再進行操作。畢竟事務(wù)成功后你才能確保之后的操作是有意義的。
Copyright©2021 w3cschool編程獅|閩ICP備15016281號-3|閩公網(wǎng)安備35020302033924號
違法和不良信息舉報電話:173-0602-2364|舉報郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號
聯(lián)系方式:
更多建議: