Django 知識庫:transaction事務
有些時候我們需要
對資料庫進行一連串的操作
,如果其中某一個操作失敗,那麼其他的操作也要跟著回滾到操作以前的狀態。
舉個例子。某天你到銀行存了 100 塊錢,所以你的賬戶的資料庫表就應該減去 100 塊,而銀行的賬戶上增加 100 塊。但如果資料庫在執行銀行賬戶增加 100 塊時操作失敗了,豈不是平白無故損失掉 100 塊錢,那你不得把銀行屋頂給拆了。
這種情況下就需要用到
事務
這個概念了,即把一組操作捆綁到一起,大家生死與共,要麼都成功,要麼都失敗,結成人民統一戰線。
Django 裡如何實現事務?看下面的例子:
# models。py
from
django。db
import
models
class
Student
(
models
。
Model
):
“”“學生”“”
name
=
models
。
CharField
(
max_length
=
20
)
class
Info
(
models
。
Model
):
“”“學生的基本情況”“”
age
=
models
。
IntegerField
()
class
Address
(
models
。
Model
):
“”“學生的家庭住址”“”
home
=
models
。
CharField
(
max_length
=
100
)
有三個模型,
Student
為學生、
Info
為學生的基本情況、
Address
為學生的住址。
假設這三個模型必須同時建立,否則資料就是不完整的。
我們可以這樣寫檢視:
def
create_student
(
request
):
student
=
Student
。
objects
。
create
(
name
=
‘張三’
)
info
=
Info
。
objects
。
create
(
age
=
19
)
address
=
Address
。
objects
。
create
(
home
=
‘北京’
)
return
HttpResponse
(
‘Create success。。。’
)
很正常對吧。接下來讓程式故意引發錯誤:
def
create_student
(
request
):
student
=
Student
。
objects
。
create
(
name
=
‘張三’
)
info
=
Info
。
objects
。
create
(
age
=
19
)
# 引發錯誤
oh_my_god
=
int
(
‘abc’
)
address
=
Address
。
objects
。
create
(
home
=
‘北京’
)
return
HttpResponse
(
‘Create success。。。’
)
這就有問題了,前面的
Student
和
Info
都正常儲存進資料庫了,但是
Address
卻由於前一句報錯而沒有執行建立,因此學生資訊就變成了不完整的垃圾資料了。
解決辦法就是把檢視函式中的資料操作轉化為
事務
:
from
django。db
import
transaction
# 注意這個裝飾器
@transaction。atomic
def
create_student
(
request
):
student
=
Student
。
objects
。
create
(
name
=
‘張三’
)
info
=
Info
。
objects
。
create
(
age
=
19
)
oh_my_god
=
int
(
‘abc’
)
address
=
Address
。
objects
。
create
(
home
=
‘北京’
)
return
HttpResponse
(
‘Create success。。。’
)
這就非常不同了。無論視圖裡哪一個資料庫操作失敗或是沒有執行,那麼其他的操作也都會回滾到操作前的狀態。也就是說上面這段程式碼中的三個模型,都沒有儲存成功。
有的時候視圖裡有很多的資料操作,如果我只想回滾其中一部分為事務也是有辦法的:
from
django。db
import
transaction
@transaction。atomic
def
create_student
(
request
):
student
=
Student
。
objects
。
create
(
name
=
‘張三’
)
# 回滾儲存點
save_tag
=
transaction
。
savepoint
()
try
:
info
=
Info
。
objects
。
create
(
age
=
19
)
# 引發錯誤
oh_my_god
=
int
(
‘abc’
)
address
=
Address
。
objects
。
create
(
home
=
‘北京’
)
except
:
# 回滾到 save_tag 的位置
transaction
。
savepoint_rollback
(
save_tag
)
return
HttpResponse
(
‘Create success。。。’
)
上面的程式碼執行之後,
Student
表會成功儲存,而另外兩張表則都會失敗。使用
try
的好處在於前端能正常執行。
除此之外,還有另一種方法可以將檢視中的事務進行分組,實現更細膩的控制:
# 裝飾器不要了
# @transaction。atomic
def
create_student
(
request
):
student
=
Student
。
objects
。
create
(
name
=
‘張三’
)
# 事務
with
transaction
。
atomic
:
info
=
Info
。
objects
。
create
(
age
=
19
)
# 引發錯誤
oh_my_god
=
int
(
‘abc’
)
address
=
Address
。
objects
。
create
(
home
=
‘北京’
)
return
HttpResponse
(
‘Create success。。。’
)
效果是差不多的,僅有
Student
成功儲存。
還有最後一個大殺器。如果你想讓所有的資料庫操作都是事務,那就在
settings。py
裡配置:
# settings。py
# 以 sqlite 為例
DATABASES
=
{
‘default’
:
{
‘ENGINE’
:
。。。
,
‘NAME’
:
。。。
,
# 加上這條
‘ATOMIC_REQUESTS’
:
True
,
}
}
然後可以用
non_atomic_requests
標記不需要成為事務的檢視:
@transaction。non_atomic_requests
def
create_student
(
request
):
。。。
另外,
類檢視
也是可以成為事務的:
class
CreateStudent
(
View
):
@transaction。atomic
def
get
(
self
,
request
):
。。。
最後總結一下,並非任意對資料庫的操作序列都是事務。資料庫事務擁有 ACID特性:
原子性(Atomicity)
:事務作為一個整體被執行,包含在其中的對資料庫的操作要麼全部被執行,要麼都不執行。
一致性(Consistency)
:事務應確保資料庫的狀態從一個一致狀態轉變為另一個一致狀態。一致狀態的含義是資料庫中的資料應滿足完整性約束。
隔離性(Isolation)
:多個事務併發執行時,一個事務的執行不應影響其他事務的執行。
永續性(Durability)
:已被提交的事務對資料庫的修改應該永久儲存在資料庫中。
關聯官方文件:Database transactions