Django數據庫--事務及事務回滾

數據庫的讀寫操做中,事務在保證數據的安全性和一致性方面起着關鍵的做用,而回滾正是這裏面的核心操做。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

相關文章
相關標籤/搜索