| revision-up-to: | 11321 (1.1) unfinished |
|---|
Django はトランザクションをサポートしているデータベース向けに、トランザクショ ン管理を制御する方法をいくつか提供しています。
Django のデフォルトの挙動では、組み込みのデータ変更に関わるモデル関数を呼び 出したときにはいつでも自動的に commit を行います。例えば、 model.save() や model.delete() を呼び出すと、変更は即座にコミットされます。
これはほとんどのデータベースにおける自動コミット設定とほとんど同じ挙動です。 すなわち、ユーザがデータベースへの書き込みを必要とするような操作を行うと、 Django はすぐに INSERT/UPDATE/DELETE 文を実行し、次いで COMMIT を実行します。暗黙のロールバックは行いません。
TransactionMiddleware を介してリクエストとレスポンスのフェイズにトラン ザクションを結び付けるというものです。
このトランザクション処理は次のように行われます: まず、リクエスト処理の開始 時にトランザクションを開始します。レスポンスを問題なく生成できたら、全ての トランザクションをコミットします。ビュー関数が例外を送出したら、ロールバッ クを起こします。
この機能を有効にするには、 TransactionMiddleware ミドルウェアを MIDDLEWARE_CLASSES 設定に追加します:
MIDDLEWARE_CLASSES = (
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.cache.CacheMiddleware',
'django.middleware.transaction.TransactionMiddleware',
)
ミドルウェアの配置順はとても重要です。トランザクションミドルウェアは呼び出 されるビュー関数だけでなく、後続のミドルウェアモジュール全てに影響します。 従って、セッションミドルウェアをトランザクションミドルウェアの後ろに配置す ると、セッションの生成はトランザクションの一部に入ってしまいます。
例外は CacheMiddleware です。このミドルウェアは影響を受けません。 キャッシュミドルウェア自体は独自のデータベースカーソル (このカーソルは、内 部的には独自のデータベース接続上にあります) を使います。
ほとんどのユーザにとって、非明示的なリクエストベースのトランザクションは素 晴らしい働きをすることでしょう。しかしながら、トランザクションの管理方法を より詳細に制御したい場合、Python の関数デコレータを使って特定の関数のトラン ザクション処理を変更できます。
ノート
下記ではビュー関数を例に取ってはいますが、以下に述べるデコレータはビュー 関数でないものにも適用できます。
ビュー関数のトランザクションの挙動を、グローバルな設定に関係なく Django の デフォルトの挙動にスイッチするには、 autocommit デコレータを使います。
例えば:
from django.db import transaction
@transaction.autocommit
def viewfunc(request):
....
viewfunc() の中では、 model.save() や model.delete() 、その他デー タベースに書き込みを行う全ての関数でトランザクションを commit します。
commit_on_success デコレータを使うと、関数内の全ての処理にわたるトラン ザクションを使えます:
from django.db import transaction
@transaction.commit_on_success
def viewfunc(request):
....
関数の実行に成功すると、 Django はそれまでの全ての作業を commit します。関 数が例外を送出すると、 Django はトランザクションを rollback します。
トランザクションを完全に管理したい場合には、 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()
以前の Django リリースを使っていたユーザへの重要なお知らせ:
データベースの connection.commit() や connection.rollback() と いった関数 (0.91 以前では db.commit() や db.rollback() と呼ばれ ていました) はなくなり、 transaction.commit() および transaction.rollback() コマンドに置き換わりました。
制御マニアの人は、 Django 設定ファイルで DISABLE_TRANSACTION_MANAGEMENT を True に設定すれば、全ての自動トランザクション管理を無効にし、自分で トランザクションを管理できます。
この場合、 Django はいかなるトランザクション管理も行わなくなります。ミドル ウェアが非明示的にトランザクションを commit することはなくなり、自分でロー ル管理を行わねばなりません。さらに、何らかのミドルウェアで変更の commit を 自分で行わねばならなくなります。
従って、トランザクションの無効化は、自作のトランザクション制御ミドルウェア を実行したい場合や、本当に変わったことをやりたい場合向けです。ほとんどの状 況では、デフォルトの挙動かトランザクションミドルウェアで十分で、必要に応じ て特定の関数だけを変更すればよいでしょう。
A savepoint is a marker within a transaction that enables you to roll back part of a transaction, rather than the full transaction. Savepoints are available to the PostgreSQL 8 and Oracle backends. Other backends will provide the savepoint functions, but they are empty operations - they won't actually do anything.
Savepoints aren't especially useful if you are using the default autocommit behaviour of Django. However, if you are using commit_on_success or commit_manually, each open transaction will build up a series of database operations, awaiting a commit or rollback. If you issue a rollback, the entire transaction is rolled back. Savepoints provide the ability to perform a fine-grained rollback, rather than the full rollback that would be performed by transaction.rollback().
Savepoints are controlled by three methods on the transaction object:
Creates a new savepoint. This marks a point in the transaction that is known to be in a "good" state.
Returns the savepoint ID (sid).
The following example demonstrates the use of savepoints:
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 はこのドキュメントの説明通りにトランザクションを処理します。
When a call to a PostgreSQL cursor raises an exception (typically IntegrityError), all subsequent SQL in the same transaction will fail with the error "current transaction is aborted, queries ignored until end of transaction block". Whilst simple use of save() is unlikely to raise an exception in PostgreSQL, there are more advanced usage patterns which might, such as saving objects with unique fields, saving using the force_insert/force_update flag, or invoking custom SQL.
There are several ways to recover from this sort of error.
The first option is to roll back the entire transaction. For example:
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
Calling transaction.rollback() rolls back the entire transaction. Any uncommitted database operations will be lost. In this example, the changes made by a.save() would be lost, even though that operation raised no error itself.
If you are using PostgreSQL 8 or later, you can use savepoints to control the extent of a rollback. Before performing a database operation that could fail, you can set or update the savepoint; that way, if the operation fails, you can roll back the single offending operation, rather than the entire transaction. For example:
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
In this example, a.save() will not be undone in the case where b.save() raises an exception.
With PostgreSQL 8.2 or later, there is an advanced option to run PostgreSQL with database-level autocommit. If you use this option, there is no constantly open transaction, so it is always possible to continue after catching an exception. For example:
a.save() # succeeds
try:
b.save() # Could throw exception
except IntegrityError:
pass
c.save() # succeeds
ノート
This is not the same as the autocommit decorator. When using database level autocommit there is no database transaction at all. The autocommit decorator still uses transactions, automatically committing each transaction when a database modifying operation occurs.
Aug 19, 2010