revision-up-to: | 17812 (1.4) |
---|
Django はトランザクションをサポートしているデータベース向けに、トランザクショ ン管理を制御する方法をいくつか提供しています。
Django のデフォルトの挙動では、組み込みのデータ変更に関わるモデル関数を呼び
出したときにはいつでも自動的に commit を行います。例えば、 model.save()
や model.delete()
を呼び出すと、変更は即座にコミットされます。
これはほとんどのデータベースにおける自動コミット設定とほとんど同じ挙動です。
すなわち、ユーザがデータベースへの書き込みを必要とするような操作を行うと、
Django はすぐに INSERT
/UPDATE
/DELETE
文を実行し、次いで
COMMIT
を実行します。暗黙のロールバックは行いません。
TransactionMiddleware
を介してリクエストとレスポンスのフェイズにトラン
ザクションを結び付けるというものです。
このトランザクション処理は次のように行われます: まず、リクエスト処理の開始 時にトランザクションを開始します。レスポンスを問題なく生成できたら、全ての トランザクションをコミットします。ビュー関数が例外を送出したら、ロールバッ クを起こします。
この機能を有効にするには、 TransactionMiddleware
ミドルウェアを
MIDDLEWARE_CLASSES
設定に追加します:
MIDDLEWARE_CLASSES = (
'django.middleware.cache.UpdateCacheMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.transaction.TransactionMiddleware',
'django.middleware.cache.FetchFromCacheMiddleware',
)
ミドルウェアの配置順はとても重要です。トランザクションミドルウェアは呼び出 されるビュー関数だけでなく、後続のミドルウェアモジュール全てに影響します。 従って、セッションミドルウェアをトランザクションミドルウェアの後ろに配置す ると、セッションの生成はトランザクションの一部に入ってしまいます。
いくつかのキャッシュミドルウェアの例外は:
CacheMiddleware
,
UpdateCacheMiddleware
,
FetchFromCacheMiddleware
は影響を受けませ
ん。データベースキャッシュを使用しているときでも、 Django のキャッシュミドル
ウェアは独自のデータベースカーソルを使います。(内部的に独自のデータベース
コネクションとマッピングされています。)
ほとんどのユーザにとって、非明示的なリクエストベースのトランザクションは素
晴らしい働きをすることでしょう。しかしながら、トランザクションの管理方法を
より詳細に制御したい場合、 django.db.transaction
の中の関数群を使って
関数ごと、またはコードブロックベースごとにトランザクションを管理してください。
これらの関数は、下記で詳細に説明しています。二つの方法で使えます。:
特定の関数の デコレーター としての一例は:
from django.db import transaction
@transaction.commit_on_success
def viewfunc(request):
# ...
# this code executes inside a transaction
# ...
コンテキストマネージャー として特定のブロックに使うには
from django.db import transaction
def viewfunc(request):
# ...
# this code executes using default transaction management
# ...
with transaction.commit_on_success():
# ...
# this code executes inside a transaction
# ...
両方のテクニックは、 Python の全てのサポートバージョンで使えます。
しかしながら、 Python 2.5 では、 with
文を使う際には
from __future__ import with_statement
をモジュールの先頭で
加えなければ行けません。
Note
下記ではビュー関数を例に取ってはいますが、以下に述べるデコレータと コンテキストマネージャーはトランザクションを扱う必要がある全ての コードで使用することが出来ます。
autocommit
()¶ビュー関数のトランザクションの挙動を、グローバルな設定に関係なく Django の
デフォルトの挙動にスイッチするには、 autocommit
デコレータを使います。
例えば:
from django.db import transaction
@transaction.autocommit
def viewfunc(request):
....
@transaction.autocommit(using="my_other_database")
def viewfunc2(request):
....
viewfunc()
の中では、 model.save()
や model.delete()
、その他デー
タベースに書き込みを行う全ての関数でトランザクションを commit します。
viewfunc2()
は同じ挙動になるでしょう、ですが "別のデータベース"
コネクションにです。
commit_on_success
()¶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):
....
関数の実行に成功すると、 Django はそれまでの全ての作業を commit します。関 数が例外を送出すると、 Django はトランザクションを rollback します。
commit_manually
()¶トランザクションを完全に管理したい場合には、 commit_manually
デコレータ
を使います。このデコレータは Django にユーザが自分でトランザクションを管理
しようとしていることを知らせます。
commit()
や rollback()
を行わずにデータを変更した場合は
TransactionManagementError
例外を送出します。
手動のトランザクション管理は以下のようになります:
from django.db import transaction
@transaction.commit_manually
def viewfunc(request):
...
# commit/rollback を好きなタイミングで行えます
transaction.commit()
...
# ただし、自分でちゃんとやっておくのを忘れないように!
try:
...
except:
transaction.rollback()
else:
transaction.commit()
@transaction.commit_manually(using="my_other_database")
def viewfunc2(request):
...
リリースノートを参照してください
Django はリクエストの完了より前にオープンなトランザクションを閉じなくてはいけ
ません。もし、 autocommit()
(通常のコミットモード)を使用しているか、
commit_on_success()
ならば、自動的にその処理が行われます。
しかし、手動でトランザクションを管理していると( commit_manually()
デコレータを使用していれば)、トランザクションがコミットされるか、
ロールバックするかをリクエストが終了する前に確かめておかなければいけません。
これは全てのデータベースオペレーションに適用されます、書き込み処理だけでは ありません。データベースから読み取るだけだったとしても、トランザクションは リクエストが完了する前にコミットかロールバックされてなければいけません。
制御マニアの人は、 Django 設定ファイルで DISABLE_TRANSACTION_MANAGEMENT
を True
に設定すれば、全ての自動トランザクション管理を無効にし、自分で
トランザクションを管理できます。
この場合、 Django はいかなるトランザクション管理も行わなくなります。ミドル ウェアが非明示的にトランザクションを commit することはなくなり、自分でロー ル管理を行わねばなりません。さらに、何らかのミドルウェアで変更の commit を 自分で行わねばならなくなります。
従って、トランザクションの無効化は、自作のトランザクション制御ミドルウェア を実行したい場合や、本当に変わったことをやりたい場合向けです。ほとんどの状況では、 デフォルトの挙動かトランザクションミドルウェアで十分で、必要に応じ て特定の関数だけを変更すればよいでしょう。
セーブポイントはトランザクションの中で、全てのトランザクションではなく、 トランザクションの一部をロールバックできるようにします。セーブポイントは PostgreSQL 8 や Oracle 、MySQL (バージョン 5.0.3 とそれより新しいもので InnnoDB をストレージエンジンに使っているもの)バックエンドで使えます。 他のバックエンドはセーブポイントを関数で提供します、しかしこれは空の オペレーションです – これは実際何もしません。
セーブポイントは、通常の autocommit
を Django で使っている限りこれは
役に立ちません。もし、 commit_on_success
か commit_manually
を
使っていると、それぞれの開かれたトランザクションは一連のデータベース処理として
構築され、コミットやロールバックを待ちます。もし生じた問題をロールバックしたい
のならば、完全にトランザクションをロールバックします。セーブポイントは、
transaction.rollback()
で動作する完全なロールバックを細かく
動作させられるようにします、
それぞれの関数は using
引数を取ります。これはこの処理が適用される
データベースの名前にしてください。もし using
引数がなかったら、
"default"
データベースが使われます。
セーブポイントは、トランザクションオブジェクトの三つのメソッドによって コントロールされています。:
transaction.
savepoint
(using=None)¶新しいセーブポイントを作ります。これは、 “よい”状態にある トランザクションをマークするポイントです。
セーブポイント ID (sid)を返します。
transaction.
savepoint_commit
(sid, using=None)¶最後のコミットか、セーブポイントが作られたときからの動作を含むように セーブポイントをアップデートします。
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 を使っている場合、MySQL のバージョンと使っているテーブルの形式に応じ て、テーブルがトランザクションをサポートする場合としない場合があります。 (「テーブルの形式」とは、”InnoDB” や “MyISAM” などを指します。) このドキュ メントでは MySQL のトランザクションにまつわる説明はしませんが、 MySQL のサ イトには トランザクションに関する情報 が掲載されています。
MySQL 構成がトランザクションをサポート していない 場合、 Django は自動コ ミットモードで動作します。すなわち、 SQL 文は呼び出されたその場で実行され、 コミットされます。 MySQL 構成がトランザクションをサポートしている場合、 Django はこのドキュメントの説明通りにトランザクションを処理します。
PostgreSQL のカーソルが例外を呼び出した時(典型的には IntegrityError
)、
同じトランザクションの中の以後全ての SQL がエラーにより失敗します “トランザ
クションブロックの終わりまでクエリは無視され、最新のトランザクションは
破棄されます”。一方で save()
は PostgreSQL の例外処理とは似ていません、
そこではもっと進歩して慣用的なパターンがあり、ユニークフィールドでオブジェクト
をセーブするようなものであったり、 force_insert/force_update フラグを使って
セーブしたり、またはカスタム SQL の
この種のエラーからの回復方法はいくつかあります。
トランザクションを全て巻き戻すための最初のオプション例です:
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()
による変更は失われます、ですがこの手続きはエラーを出しません。
もし、 PostgreSQL 8 か、それより新しいものを使っていれば、 セーブポイント を使って データベースが動作する前、失敗するまえに戻せます、セーブポイントを アップグレードしたり設定することが出来ます。この方法は、オペレーションが 失敗した時に、一つ前の手続きにもどることができ、完全にトランザクションを 巻き戻さなくて済みます。:
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()
が
例外を発生させています。
PostgreSQL 8.2 かそれより新しいと、進歩したオプションが PostgreSQLを実行 するためにあります。 データベースレベル オートコミット です。 もしこのオプションを使うと、恒常的にトランザクションが開かなくなり、 例外を捕まえた後でも処理を継続できるようになります。:
a.save() # succeeds
try:
b.save() # Could throw exception
except IntegrityError:
pass
c.save() # succeeds
Note
これは、 オートコミットデコレータ
と同じではなく、データベースレベルでのオートコミットを使う時に、それは
データベースのトランザクションは全くありません。 オートコミット
でコレーッタはトランザクションを使い、データベースが変化する処理が生じた
時に、自動的にそれぞれのトランザクションをコミットします。
Oct 26, 2017