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