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


數據庫的讀寫操作中,事務在保證數據的安全性和一致性方面起着關鍵的作用,而回滾正是這里面的核心操作。Django的ORM在事務方面也提供了不少的API。有事務出錯的整體回滾操作,也有基於保存點的部分回滾。本文將討論Django中的這兩種機制的運行原理。

 

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.

 

主要函數:

1. get_autocommit(using=None)   

判斷事務是否自動提交

2. set_autocommit(autocommit, using=None)  

設置自動提交事務

 

這些函數使接受一個 using 參數表示所要操作的數據庫。如果未提供,則 Django 使用 "default" 數據庫。

 

3. on_commit(do something)

事務提交后馬上執行任務,例如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):

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才會有數據

 

 

 

二、保存點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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM