Django 事務小結

官方關於事務的說明

Django 默認的事務行爲(Django’s default transaction behavior)

Django 默認狀況下是運行一個打開的事務,這個事務在 model 中引發了數據變更的內置函式被調用時,就會被自動提交。例如,若是你調用 model.save() 或 model.delete(),改動就會被自動提交。python

這與不少數據庫中的自動提交事務的設置類似。只要你執行了寫數據庫的動做,Django 就會生成對應的 INSERT/UPDATE/DELETE 語句,而後執行 COMMIT。這其中並無暗含 ROLLBACK 回滾操做。web

將 HTTP 請求與事務進行綁定(Tying transactions to HTTP requests)

這種方式適用於在 web 請求中處理事務的狀況,經過 TransactionMiddleware 將 web 請求/ web 應答與事務進行綁定。sql

工做流程如此:當 Django 收到一個請求時,就會對某個事務進行處理。若是在生成應答的過程當中沒有出現問題,Django 就會提交未結束的事務。若是視圖函式產生了異常,Django 就會回滾未結束的事務。數據庫

要激活這個特性,只要將 TransactionMiddleware 中間件添加到 MIDDLEWARE_CLASSES 設置便可:django

MIDDLEWARE_CLASSES = (
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.cache.CacheMiddleware',
    'django.middleware.transaction.TransactionMiddleware',
)

這個順序是至關重要的。事務中間件不只僅只做用於視圖函式,也對排在它後面的全部中間件起做用。若是你將 session 中間件放在事務中間件的後面,那麼建立 session 也會變成事務的一部分。緩存

CacheMiddleware 是一個例外,事務不會對它起做用。這是由於緩存中間件使用它本身的數據庫遊標(其內部使用本身的數據庫遊標)。session

在視圖中控制事務管理(Controlling transaction management in views)

對於大多數人來講,隱式的基於請求的事務工做得很是好。可是,若是你須要對如何管理事務作更細緻的控制,那麼你可使用 Python 裝飾器來改變這種由視圖函式處理事務的方式。less

注意spa

雖然下面的例子使用視圖函式作爲例子,但這些裝飾器也能夠做用於非視圖函式。翻譯

django.db.transaction.autocommit

使用 autocommit 裝飾器時,會忽略全局事務設置,將某個視圖函式轉換化 Django 默認的事務提交行爲。

例如:

from django.db import transaction

@transaction.autocommit
def viewfunc(request):
    ....

在 viewfunc() 裏,一旦你調用了 model.save(), model.delete(),或是任何其餘會寫數據庫的方法,事務就會被馬上提交。

django.db.transaction.commit_on_success

使用 commit_on_success 裝飾器時,會令某個函式中每一項工做完成後都使用一個單獨的事務:

from django.db import transaction

@transaction.commit_on_success
def viewfunc(request):
    ....

若是這個函式成功返回,接下來 Django 就會提交函式中全部已完成的工做。若是函式拋出異常,Django 就會對事務進行回滾。

django.db.transaction.commit_manually

若是須要對事務進行全面控制,可使用 commit_manually 裝飾器。它告訴 Django 你將全面接管這個事務。

若是你的視圖修改了數據卻沒有 commit() 或 rollback(), Django 將拋出 TransactionManagementError 異常。

手動管理事務以下:

from django.db import transaction

@transaction.commit_manually
def viewfunc(request):
    ...
    # You can commit/rollback however and whenever you want
    transaction.commit()
    ...

    # But you've got to remember to do it yourself!
    try:
        ...
    except:
        transaction.rollback()
    else:
        transaction.commit()

早期 Django 版本的用戶請注意:

數據庫 connection.commit() 和 connection.rollback() 方法(在 Djanog 0.91 以及更早版本中被稱爲 db.commit() 和 db.rollback() ) 已不在再在。它們已經被 transaction.commit() 和 transaction.rollback() 替換。

如何使全局事務管理無效(How to globally deactivate transaction management)

在 Django 的配置文件中將 DISABLE_TRANSACTION_MANAGEMENT 設爲 True 就會禁用全部的事務管理。

若是你如法設置,那麼 Django 就再也不自動提供任何的事務管理;中間件也再也不隱式地提交事務,並且你須要自行去管理事務。甚至於你還要爲其餘的中間件提交事務。

所以,若是你想使用你本身寫的事務中間件或是你想作一些很是古怪的東西,那麼如上所作確實是最佳選擇。可是在大多數狀況下,你最好仍是關閉默認的事務行爲或去掉事務中間件,而後只修改必要的函式。

保存點(Savepoints)

保存點是事務內的一個標記,用來確保你能回滾部分事務,而不是回滾整個事務。保存點對 PostgreSQL 8 和 Oracle 數據庫是很是有用的。其餘數據庫也提供 savepoint 函式,可是卻只是個空操做,不會執行任何操做。

若是你使用 Django 默認的autocommit 事務行爲,那麼保存點功能並非特別有用。可是若是你使用 commit_on_success 或 commit_manually,每一個打開的事務都積累了一系列數據庫操做,等待提交或是回滾。若是你要求回滾,那麼整個的事務都會被回滾。保存點提供了這樣一種能力,它能操做回滾的細節,作部分回滾,而不是使用 transaction.rollback() 一次性所有回滾。

在事務對象中,保存點由三個方法控制:

transaction.savepoint()

建立一個新的保存點,它在事務標記一個點,代表當前是 "good" 狀態。

返回保存點的 ID (sid).

transaction.savepoint_commit(sid)

更新保存點。它對自保存點被建立以後,事務中執行的全部操做進行保存;或是對上次提交以後的全部操做進行保存。

transaction.savepoint_rollback(sid)

將事務回滾到最後一次提交時的保存點。

下面這些例子演示了保存點的用法:

from django.db import transaction

@transaction.commit_manually
def viewfunc(request):

  a.save()
  # open transaction now contains a.save()
  sid = transaction.savepoint()

  b.save()
  # open transaction now contains a.save() and b.save()

  if want_to_keep_b:
      transaction.savepoint_commit(sid)
      # open transaction still contains a.save() and b.save()
  else:
      transaction.savepoint_rollback(sid)
      # open transaction now contains only a.save()

  transaction.commit()

MySQL 中的事務(Transactions in MySQL)

若是你正在使用 MySQL,那麼你的表可能支持,也可能不支持事務;這取決於你的 MySQL 版本和表的類型(這裏的表類型,就是指 "InnoDB" 或 "MyISAM")。 MySQL 的事務特性已經超出了本文所討論的範圍,若是您感興趣,能夠查看 information on MySQL transactions。

若是你的 MySQL 不支持事務,那麼 Django 將在 auto-commit 模式中實現這個功能:調用任何語句都被馬上被運行,而後提交。若是你的 MySQL 支持事務,Django 將使用上文中所提到的方法來處理事務。

在 PostgreSQL 事務中處理異常(Handling exceptions within PostgreSQL transactions)

在調用 PostgreSQL 標遊時,若是拋出異常 (好比 IntegrityError),同個事務中的全部 SQL 子查詢都會曗一樣的錯誤 "當前事務被中斷,查詢將被忽視,直至事務結束 current transaction is aborted, queries ignored until end of transaction block"。只是簡單地使用 save() 並不會在 PostgreSQL 中拋開異常,這一般出如今使用更先進的特性時,好比保存含有惟一字段的對象,或保存時使用了 force_insert/force_update 標識,或是使用自定義 SQL 。

下面幾種方法能夠避免這種錯誤。

事務回滾(Transaction rollback)

第一種方法就是回滾整個事務。例如:

a.save() # Succeeds, but may be undone by transaction rollback
try:
    b.save() # Could throw exception
except IntegrityError:
    transaction.rollback()
c.save() # Succeeds, but a.save() may have been undone

調用 transaction.rollback() 回滾整個事務。任何未提交的數據庫操做都會丟失。在這個例子中, a.save() 所作的修改將丟失,並且不會拋出任何異常。

保存點回滾(Savepoint rollback)

若是你正在使用 PostgreSQL 8 或是更新版本,你可使用 savepoints 來控制回滾範圍。在執行一個可能會失敗的數據庫操做以前,你能夠設置或是更新保存點。若是這個操做失敗了,你可回滾到上一個提交的操做,而不是回滾整個事務。例如:

a.save() # Succeeds, and never undone by savepoint rollback
try:
    sid = transaction.savepoint()
    b.save() # Could throw exception
    transaction.savepoint_commit(sid)
except IntegrityError:
    transaction.savepoint_rollback(sid)
c.save() # Succeeds, and a.save() is never undone

在這個例子中,a.save() 將不會被回滾,而 b.save() 會拋出異常。

數據庫級的自動提交(Database-level autocommit)

在使用 PostgreSQL 8.2 或更新的版本時,可使用 PostgreSQL 自帶的一個先進特性 database-level autocommit。若是使用了這個特性,就不會出現打開而未提交的事務,因此即便使用了異常捕牛,它仍會繼續運行。例如:

a.save() # succeeds
try:
    b.save() # Could throw exception
except IntegrityError:
    pass
c.save() # succeeds

Django進行事務管理


1.基於Django ORM的transaction處理


2.是基於自定義SQL 語句的transaction的處理

一般是比較複雜的SQL,用ORM 處理不方便的時候用的。或者是大批量SQL語句執行,比較在乎效率的狀況下用。
首先說一下第二種狀況,由於這種狀況相對簡單一點,沒ORM 那麼多東西,用我寫的一個方法來解釋

from django.db import connection, transaction
.....
def batch_execsql(sqlarray):
    cursor = connection.cursor() # 獲得處理的遊標對象
    ret=""
    try:
        for sql in sqlarray:
            cursor.execute(sql)
        transaction.commit_unless_managed() # 這是重點,沒有這條語句,就不會commit 。
    except Exception,e: #簡單的異常處理,能夠忽略
        ret=str(e)
    cursor.close() 
    return ret #有異常則返回異常,不然返回爲空字符串

由上面能夠看出 transaction.commit_unless_managed()的重要性,這是自定義SQL 語句狀況下處理事務的方法. 上面的例子中的 sqlarray 表示一個list,裏面有不少本身寫的SQL 語句,而這些語句要求在一個事務中完成。

再來看看第一種狀況,用ORM 的時候,事務的處理. 這在django 的官方文檔中有說明,下面簡單翻譯介紹下
1. django 默認的事務, 是自動處理的,當你在調用 ORM 的model.save(),model.delete()的時候,全部改動會被當即提交的,至關於數據庫設置了auto commit,沒有隱藏的rollback.

2.對http請求的事務攔截,這是推薦的方式,使用了transaction中間件來完成,這是比較好的方法,但必須在settings.py中配置.

MIDDLEWARE_CLASSES = (
    'django.middleware.cache.UpdateCacheMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.transaction.TransactionMiddleware',
    'django.middleware.cache.FetchFromCacheMiddleware',
)

但須要注意的是,這樣配置以後,與你中間件的配置順序是有很大關係的。在 TransactionMiddleware 以後的全部中間件都會受到事務的控制。因此當你把session 中間件放到 Transaction以後,一樣會受到影響。但  CacheMiddleware, UpdateCacheMiddleware, and FetchFromCacheMiddleware  不會受到影響,cache機制有本身的處理方式,用了內部的connection來處理

另外 TransactionMiddleware 只對 default 的數據庫配置有效,若是要對另外的數據鏈接用這種方式,必須本身實現中間件。

 

3.本身來控制事務

這種狀況下,你本身靈活控制事務.在settings.py 中不用配置 TransactionMiddleware 中間件了, 基本採用裝飾模式來實現。
a)@transaction.autocommit ,django默認的事務處理, 採用此裝飾模式會忽略掉全局的transaction 設置

from django.db import transaction

@transaction.autocommit
def viewfunc(request):
    ....

@transaction.autocommit(using="my_other_database")
def viewfunc2(request):
    ....

b) @transaction.commit_on_success 在一個方法中,全部工做完成後,提交事務。

from django.db import transaction

@transaction.commit_on_success
def viewfunc(request):
    ....

@transaction.commit_on_success(using="my_other_database")
def viewfunc2(request):
    ....

c) commit_manually() ,徹底本身處理,但若是你沒有調用commit()或者rollback(),將會拋出TransactionManagementError 異常.

from django.db import transaction

@transaction.commit_manually
def viewfunc(request):
    ...
    # You can commit/rollback however and whenever you want
    transaction.commit()
    ...

    # But you've got to remember to do it yourself!
    try:
        ...
    except:
        transaction.rollback()
    else:
        transaction.commit()

@transaction.commit_manually(using="my_other_database")
def viewfunc2(request):
    ....
相關文章
相關標籤/搜索