數據庫的讀寫操做中,事務在保證數據的安全性和一致性方面起着關鍵的做用,而回滾正是這裏面的核心操做。Django的ORM在事務方面也提供了很多的API。有事務出錯的總體回滾操做,也有基於保存點的部分回滾。本文將討論Django中的這兩種機制的運行原理。python
Django利用django.db.transaction模塊中的API對數據庫進行事務的管理數據庫
Django provides a straightforward API in the django.db.transaction module to manage the autocommit state of each database connection.django
主要函數:後端
1. get_autocommit(using=None) 安全
判斷事務是否自動提交網絡
2. set_autocommit(autocommit, using=None) ide
設置自動提交事務函數
這些函數使接受一個 using 參數表示所要操做的數據庫。若是未提供,則 Django 使用 "default" 數據庫。測試
3. on_commit(do something)atom
事務提交後立刻執行任務,例如celery任務
例如:
with transation.atomic:
#do something and commit the transaction
transaction.on_commit(lambda: some_celery_task.delay('arg1'))
怎麼使用?在哪裏使用?
事務是一系列的數據庫操做,在數據的安全性和減小網絡請求方面都有很大的優點。關於數據庫事務的文章有不少,我這裏就不展開討論了。
那麼ORM中有哪些相關的API呢?
trasation模塊中最重要的是一個Atomic類,Atomic是一個上下文管理器。可使用@transaction.atomic 或者with transaction.atomic 的方式來調用。
爲了設置保存點,即斷點進行事務的執行和回滾,能夠嵌套使用with transaction.atomic,例如官網的例子(僞代碼):
with transaction.atomic(): # Outer atomic, start a new transaction transaction.on_commit(foo) #事務提交後立刻執行foo函數 try: with transaction.atomic(): # Inner atomic block, create a savepoint transaction.on_commit(bar) #事務提交後立刻執行foo函數 raise SomeError() # Raising an exception - abort the savepoint except SomeError: pass
第一個with transaction.atomic()建立事務,第二個with transaction.atomic()建立保存點。
雖然錯誤raiseSomeError是從‘內部’的保存點發出來的,但只會影響到‘外部’的保存點,即只會回滾前面的數據庫操做。
下面還會討論另外一種建立保存點的方法。
在使用transaction.atomic前須要注意的問題:
1. 數據庫的自動提交默認爲開啓,若是要將它關閉,必須很當心。一旦使用了transaction,即關閉了自動提交。
2. 若是數據庫以前的使用的是自動提交,那麼在切換爲非自動提交以前,必須確保當前沒有活動的事務,一般能夠手動執行commit() 或者 rollback() 函數來把未提交的事務提交或者回滾。
1、總體回滾
全部的數據庫更新操做都會在一個事務中執行,若是事務中任何一個環節出現錯誤,都會回滾整個事務。
案例(僞代碼1):
from django.db import transaction # open a transaction @transaction.atomic #裝飾器格式 def func_views(request): do_something() a = A() #實例化數據庫模型 try: a.save() except DatabaseError: pass
此方案整個view都會在事務之中,全部對數據庫的操做都是原子性的。
案例(僞代碼2):
from django.db import transaction def func_views(request): try: with transaction.atomic(): #上下文格式,能夠在python代碼的任何位置使用 a = A() a.save() #raise DatabaseError #測試用,檢測是否能捕捉錯誤 except DatabaseError: # 自動回滾,不須要任何操做 pass
此方案比較靈活,事務能夠在代碼中的任意地方開啓,對於事務開啓前的數據庫操做是一定會執行的,事務開啓後的數據庫操做一旦出現錯誤就會回滾。
須要注意的是:
1. python代碼中對Models的修改和對數據庫的修改的區別,數據庫層面的修改不會影響Models實例變量。
若是在代碼中修改一個變量,例如:
try: with transaction.atomic(): a = A() a.attribute = True #A表的某一個屬性(即數據庫的某一列) a.save() raise DatabaseError except DatabaseError: pass print(a.attribute)
#輸出結果:True
即便數據庫回滾了,可是a實例的變量a.attribute仍是會保存在Models實例中,若是須要修改,就須要在except DatabaseError後面進行。
2. transaction不須要在代碼中手動commit和rollback的。由於只有當一個transaction正常退出的時候,纔會對數據庫層面進行操做。除非咱們手動調用transaction.commit和transaction.rollback
實際案例(此實例用僞代碼2的格式):
models.py
數據表
class Author(models.Model): name = models.CharField(max_length=30,null=False) age = models.IntegerField() email = models.URLField(null=True) class Count(models.Model): name = models.CharField(max_length=30) article_amount = models.IntegerField()
views.py
from django.shortcuts import render from django.http import HttpResponse from index.models import Author,Count from django.db import transaction,IntegrityError def add_author_views(request): author_name = u'renyingying' author = Author(name=author_name, age=24, email='renyingying@qqq.com') # author.save() count = Count(name=author_name, article_amount=1) count.save() try: with transaction.atomic(): author.save() raise DatabaseError #報出錯誤,檢測事務是否能捕捉錯誤 except DatabaseError: # 自動回滾,不須要任何操做 pass
事務外的數據庫操做正常執行,而事務內的數據庫操做則會回滾。
author表
count表
將raise DatabaseError這一行代碼註釋掉,author纔會有數據
2、保存點Savepoint(斷點回滾)
保存點是事務中的標記,從原理實現上來講是一個相似存儲結構的類。能夠回滾部分事務,而不是完整事務,同時會保存部分事務。python後端程序可使用保存點。
一旦打開事務atomic(),就會構建一系列等待提交或回滾的數據庫操做。一般,若是發出回滾命令,則會回滾整個事務。保存點則提供了執行細粒度回滾的功能,而不是將執行的徹底回滾transaction.rollback()。
工做原理:savepoint經過對返回sid後面的將要執行的數據庫操做進行計數,並保存在內置的列表中,當對數據庫數據庫進行操做時遇到錯誤而中斷,根據sid尋找以前的保存點並回滾數據,並將這個操做從列表中刪除。
相關API:
1. savepoint(using = None)
建立一個新的保存點。這表示處於正常狀態的事務的一個點。返回保存點ID(sid)。在一個事務中能夠建立多個保存點。
2. savepoint_commit(sid,using = None)
發佈保存點sid,從建立保存點開始執行的數據庫操做將成爲可能回滾事務的一部分
3. savepoint_rollback(sid,using = None)
將事務回滾到保存點sid
4. clean_savepoints(using = None)
重置用於生成惟一保存點ID的計數器
值得注意的是:
這些函數中的每個都接受一個using參數,該參數是數據庫的名稱。若是using未提供參數,則使用"default"默認數據庫。
案例:
models.py上文的案例同樣
views.py
from django.db import transaction # open a transaction @transaction.atomic def add_author_views(request): # 自動提交方式 # Author.objects.create(name=u'wangbaoqiang',age=33,email='wangbaoqiang@qqq.com') author_name = u'linghuchong' author = Author(name=author_name,age=26,email='linghuchong@qqq.com') author.save() # transaction now contains author.save() sid = transaction.savepoint() try: count = Count(name=author_name, article_amount=1) count.save() # transaction now contains author.save() and count.save() transaction.savepoint_commit(sid) # open transaction still contains author.save() and count.save() except IntegrityError: transaction.savepoint_rollback(sid) # open transaction now contains only count.save() # 保存author操做回滾後,事務只剩下一個操做 transaction.clean_savepoints() #清除保存點
注意:但願當遇到錯誤獲得回滾的事務必定要放在try裏面(若是放在try外面,雖然不會報錯,可是是不會執行的)。如上面的例子,若是在給Count表執行插入數據發生錯誤,就會‘斷點’回滾到Count表插入數據前,Author表插入的數據不變。
結果顯示:
Author表
Count表
參考文章:
https://blog.csdn.net/m0_37422289/article/details/82221489