django.db.transaction.commit_on_successデコレータの挙動はいまいち
ちょっとハマったのでメモしておく。
commit_on_successデコレータは、その名の通り関数が成功したらコミットする。その関数の開始時にトランザクションを開始したりはしない。既存のコネクションがあればそれを利用する。したがって、commit_on_successデコレータをネストさせても、意図したようには動かない。ネストした関数が失敗すると、ネストする前に行ったDB操作も一緒にロールバックされる。きちんとソース読んで、それでもわからないなら実際にコード書かないと駄目だな・・・
次のコードだと、test_rollback_nestedだけテストにパスしない。
# -*- encoding: utf-8 -*- from django.db import models from django.db import transaction class Person(models.Model): name = models.CharField(u'名前', max_length=100, unique=True) def __unicode__(self): return self.name def test_without_tran(): """ >>> Person.objects.all().delete() >>> test_without_tran() >>> Person.objects.all().order_by('name') [<Person: Test1>] """ Person.objects.create(name="Test1") def test_transaction(): """ >>> Person.objects.all().delete() >>> test_transaction() >>> Person.objects.all().order_by('name') [<Person: Test1>, <Person: Test2>] """ @transaction.commit_on_success def _expect_commit(): Person.objects.create(name="Test1") Person.objects.create(name="Test2") try: _expect_commit() except: pass def test_rollback(): """ >>> Person.objects.all().delete() >>> test_rollback() >>> Person.objects.all().order_by('name') [] """ @transaction.commit_on_success def _expect_rollback(): Person.objects.create(name="Test1") Person.objects.create(name="Test1") try: _expect_rollback() except: pass def test_rollback_nested(): """ >>> Person.objects.all().delete() >>> test_rollback_nested() >>> Person.objects.all().order_by('name') [<Person: Test10>, <Person: Test11>] """ @transaction.commit_on_success def _expect_rollback(): Person.objects.create(name="Test1") Person.objects.create(name="Test1") @transaction.commit_on_success def _expect_commit(): Person.objects.create(name="Test10") try: _expect_rollback() except: pass Person.objects.create(name="Test11") try: _expect_commit() except: pass