djangoでアトミックトランザクションを使ってみる
djangoでアトミックトランザクションを使ってみる
途中でエラーがあった場合はデータベースの更新をやめるコミットロールバックの機能みたいです。早速使ってみたいと思います。
参考:
・アトミックトランザクション
・【Django】トランザクションの設定方法
モデル
簡単なモデルになりますが注目するところはPhotoクラスのtitleがunique=trueになっているので、同じ名前の写真名は登録できない事です。
from django.db import models
class Photographer(models.Model):
name = models.CharField(max_length=128)
class Photo(models.Model):
photographer = models.ForeignKey(Photographer,
on_delete=models.CASCADE,
related_name='photograpers')
title = models.CharField(max_length=128, unique=True)
ビュー
まずは下の通りの例です。コミットとロールバックを何もしていない例です。photo_titleにmountainが2件入ってくるので三つ目のphoto_titleをインサートしようとした時にエラーが発生します。実行してみましょう。
from django.http import HttpResponse, HttpResponseBadRequest
from myapp.models import Photographer, Photo
def regist_photo_and_photographer(request):
regist_datas = ['kyosuke', ['bird', 'mountain', 'mountain']]
photographer_name = regist_datas[0]
photo_titles = regist_datas[1]
photographer = Photographer.objects.create(name=photographer_name)
for photo_title in photo_titles:
Photo.objects.create(
photographer=photographer,
title=photo_title,
)
return HttpResponse("OK")
データベース
データベースが更新されてしまっているのがわかります。
MariaDB [djangodatabase]> select * from myapp_photographer;
+----+---------+
| id | name |
+----+---------+
| 1 | kyosuke |
+----+---------+
MariaDB [djangodatabase]> select * from myapp_photo;
+----+----------+-----------------+
| id | title | photographer_id |
+----+----------+-----------------+
| 1 | bird | 1 |
| 2 | mountain | 1 |
+----+----------+-----------------+
ビュー修正後
下記の通り2か所追記するだけでこの問題を防げます。実行してみましょう。
from django.http import HttpResponse, HttpResponseBadRequest
from myapp.models import Photographer, Photo
from django.db import transaction # 追記
@transaction.atomic # 追記
def regist_photo_and_photographer(request):
regist_datas = ['kyosuke', ['bird', 'mountain', 'mountain']]
photographer_name = regist_datas[0]
photo_titles = regist_datas[1]
photographer = Photographer.objects.create(name=photographer_name)
for photo_title in photo_titles:
Photo.objects.create(
photographer=photographer,
title=photo_title,
)
return HttpResponse("OK")
データベース
下記の通りデータベースに値が挿入されていないのが確認できました。
MariaDB [djangodatabase]> select * from myapp_photographer;
Empty set (0.00 sec)
MariaDB [djangodatabase]> select * from myapp_photo;
Empty set (0.00 sec)
その他の方法
その他の方法が2つあるみたいだったので載せておきたいと思います。
【コンテキストマネージャを使用する方法】
アノテーションを使わない場合はあ下記の通りに記述する事で同じくデータベースの更新を防げます。
def regist_photo_and_photographer(request):
with transaction.atomic():
regist_datas = ['kyosuke', ['bird', 'mountain', 'mountain']]
photographer_name = regist_datas[0]
photo_titles = regist_datas[1]
photographer = Photographer.objects.create(name=photographer_name)
for photo_title in photo_titles:
Photo.objects.create(
photographer=photographer,
title=photo_title,
)
return HttpResponse("OK")
【ATOMIC_REQUESTSを使用する方法】
下記の通りsettings.pyを編集する方法です。しかしこれを記述してしまうとview全体に反映してしまうそうです。詳しくは参考サイトから。
# settings.py
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
'ATOMIC_REQUESTS': True, # 追加
}
}
ディスカッション
コメント一覧
まだ、コメントがありません