/tests/modeltests/transactions/tests.py

https://github.com/ahmadmoalla/django · Python · 313 lines · 241 code · 13 blank · 59 comment · 3 complexity · 95a6c0c8f8f84e385bee37df20e8391c MD5 · raw file

  1. from __future__ import with_statement
  2. import sys
  3. from django.db import connection, transaction, IntegrityError, DEFAULT_DB_ALIAS
  4. from django.conf import settings
  5. from django.test import TransactionTestCase, skipUnlessDBFeature
  6. from models import Reporter
  7. class TransactionTests(TransactionTestCase):
  8. def create_a_reporter_then_fail(self, first, last):
  9. a = Reporter(first_name=first, last_name=last)
  10. a.save()
  11. raise Exception("I meant to do that")
  12. def remove_a_reporter(self, first_name):
  13. r = Reporter.objects.get(first_name="Alice")
  14. r.delete()
  15. def manually_managed(self):
  16. r = Reporter(first_name="Dirk", last_name="Gently")
  17. r.save()
  18. transaction.commit()
  19. def manually_managed_mistake(self):
  20. r = Reporter(first_name="Edward", last_name="Woodward")
  21. r.save()
  22. # Oops, I forgot to commit/rollback!
  23. @skipUnlessDBFeature('supports_transactions')
  24. def test_autocommit(self):
  25. """
  26. The default behavior is to autocommit after each save() action.
  27. """
  28. self.assertRaises(Exception,
  29. self.create_a_reporter_then_fail,
  30. "Alice", "Smith"
  31. )
  32. # The object created before the exception still exists
  33. self.assertEqual(Reporter.objects.count(), 1)
  34. @skipUnlessDBFeature('supports_transactions')
  35. def test_autocommit_decorator(self):
  36. """
  37. The autocommit decorator works exactly the same as the default behavior.
  38. """
  39. autocomitted_create_then_fail = transaction.autocommit(
  40. self.create_a_reporter_then_fail
  41. )
  42. self.assertRaises(Exception,
  43. autocomitted_create_then_fail,
  44. "Alice", "Smith"
  45. )
  46. # Again, the object created before the exception still exists
  47. self.assertEqual(Reporter.objects.count(), 1)
  48. @skipUnlessDBFeature('supports_transactions')
  49. def test_autocommit_decorator_with_using(self):
  50. """
  51. The autocommit decorator also works with a using argument.
  52. """
  53. autocomitted_create_then_fail = transaction.autocommit(using='default')(
  54. self.create_a_reporter_then_fail
  55. )
  56. self.assertRaises(Exception,
  57. autocomitted_create_then_fail,
  58. "Alice", "Smith"
  59. )
  60. # Again, the object created before the exception still exists
  61. self.assertEqual(Reporter.objects.count(), 1)
  62. @skipUnlessDBFeature('supports_transactions')
  63. def test_commit_on_success(self):
  64. """
  65. With the commit_on_success decorator, the transaction is only committed
  66. if the function doesn't throw an exception.
  67. """
  68. committed_on_success = transaction.commit_on_success(
  69. self.create_a_reporter_then_fail)
  70. self.assertRaises(Exception, committed_on_success, "Dirk", "Gently")
  71. # This time the object never got saved
  72. self.assertEqual(Reporter.objects.count(), 0)
  73. @skipUnlessDBFeature('supports_transactions')
  74. def test_commit_on_success_with_using(self):
  75. """
  76. The commit_on_success decorator also works with a using argument.
  77. """
  78. using_committed_on_success = transaction.commit_on_success(using='default')(
  79. self.create_a_reporter_then_fail
  80. )
  81. self.assertRaises(Exception,
  82. using_committed_on_success,
  83. "Dirk", "Gently"
  84. )
  85. # This time the object never got saved
  86. self.assertEqual(Reporter.objects.count(), 0)
  87. @skipUnlessDBFeature('supports_transactions')
  88. def test_commit_on_success_succeed(self):
  89. """
  90. If there aren't any exceptions, the data will get saved.
  91. """
  92. Reporter.objects.create(first_name="Alice", last_name="Smith")
  93. remove_comitted_on_success = transaction.commit_on_success(
  94. self.remove_a_reporter
  95. )
  96. remove_comitted_on_success("Alice")
  97. self.assertEqual(list(Reporter.objects.all()), [])
  98. @skipUnlessDBFeature('supports_transactions')
  99. def test_commit_on_success_exit(self):
  100. @transaction.autocommit()
  101. def gen_reporter():
  102. @transaction.commit_on_success
  103. def create_reporter():
  104. Reporter.objects.create(first_name="Bobby", last_name="Tables")
  105. create_reporter()
  106. # Much more formal
  107. r = Reporter.objects.get()
  108. r.first_name = "Robert"
  109. r.save()
  110. gen_reporter()
  111. r = Reporter.objects.get()
  112. self.assertEqual(r.first_name, "Robert")
  113. @skipUnlessDBFeature('supports_transactions')
  114. def test_manually_managed(self):
  115. """
  116. You can manually manage transactions if you really want to, but you
  117. have to remember to commit/rollback.
  118. """
  119. manually_managed = transaction.commit_manually(self.manually_managed)
  120. manually_managed()
  121. self.assertEqual(Reporter.objects.count(), 1)
  122. @skipUnlessDBFeature('supports_transactions')
  123. def test_manually_managed_mistake(self):
  124. """
  125. If you forget, you'll get bad errors.
  126. """
  127. manually_managed_mistake = transaction.commit_manually(
  128. self.manually_managed_mistake
  129. )
  130. self.assertRaises(transaction.TransactionManagementError,
  131. manually_managed_mistake)
  132. @skipUnlessDBFeature('supports_transactions')
  133. def test_manually_managed_with_using(self):
  134. """
  135. The commit_manually function also works with a using argument.
  136. """
  137. using_manually_managed_mistake = transaction.commit_manually(using='default')(
  138. self.manually_managed_mistake
  139. )
  140. self.assertRaises(transaction.TransactionManagementError,
  141. using_manually_managed_mistake
  142. )
  143. class TransactionRollbackTests(TransactionTestCase):
  144. def execute_bad_sql(self):
  145. cursor = connection.cursor()
  146. cursor.execute("INSERT INTO transactions_reporter (first_name, last_name) VALUES ('Douglas', 'Adams');")
  147. transaction.set_dirty()
  148. @skipUnlessDBFeature('requires_rollback_on_dirty_transaction')
  149. def test_bad_sql(self):
  150. """
  151. Regression for #11900: If a function wrapped by commit_on_success
  152. writes a transaction that can't be committed, that transaction should
  153. be rolled back. The bug is only visible using the psycopg2 backend,
  154. though the fix is generally a good idea.
  155. """
  156. execute_bad_sql = transaction.commit_on_success(self.execute_bad_sql)
  157. self.assertRaises(IntegrityError, execute_bad_sql)
  158. transaction.rollback()
  159. class TransactionContextManagerTests(TransactionTestCase):
  160. def create_reporter_and_fail(self):
  161. Reporter.objects.create(first_name="Bob", last_name="Holtzman")
  162. raise Exception
  163. @skipUnlessDBFeature('supports_transactions')
  164. def test_autocommit(self):
  165. """
  166. The default behavior is to autocommit after each save() action.
  167. """
  168. with self.assertRaises(Exception):
  169. self.create_reporter_and_fail()
  170. # The object created before the exception still exists
  171. self.assertEqual(Reporter.objects.count(), 1)
  172. @skipUnlessDBFeature('supports_transactions')
  173. def test_autocommit_context_manager(self):
  174. """
  175. The autocommit context manager works exactly the same as the default
  176. behavior.
  177. """
  178. with self.assertRaises(Exception):
  179. with transaction.autocommit():
  180. self.create_reporter_and_fail()
  181. self.assertEqual(Reporter.objects.count(), 1)
  182. @skipUnlessDBFeature('supports_transactions')
  183. def test_autocommit_context_manager_with_using(self):
  184. """
  185. The autocommit context manager also works with a using argument.
  186. """
  187. with self.assertRaises(Exception):
  188. with transaction.autocommit(using="default"):
  189. self.create_reporter_and_fail()
  190. self.assertEqual(Reporter.objects.count(), 1)
  191. @skipUnlessDBFeature('supports_transactions')
  192. def test_commit_on_success(self):
  193. """
  194. With the commit_on_success context manager, the transaction is only
  195. committed if the block doesn't throw an exception.
  196. """
  197. with self.assertRaises(Exception):
  198. with transaction.commit_on_success():
  199. self.create_reporter_and_fail()
  200. self.assertEqual(Reporter.objects.count(), 0)
  201. @skipUnlessDBFeature('supports_transactions')
  202. def test_commit_on_success_with_using(self):
  203. """
  204. The commit_on_success context manager also works with a using argument.
  205. """
  206. with self.assertRaises(Exception):
  207. with transaction.commit_on_success(using="default"):
  208. self.create_reporter_and_fail()
  209. self.assertEqual(Reporter.objects.count(), 0)
  210. @skipUnlessDBFeature('supports_transactions')
  211. def test_commit_on_success_succeed(self):
  212. """
  213. If there aren't any exceptions, the data will get saved.
  214. """
  215. Reporter.objects.create(first_name="Alice", last_name="Smith")
  216. with transaction.commit_on_success():
  217. Reporter.objects.filter(first_name="Alice").delete()
  218. self.assertQuerysetEqual(Reporter.objects.all(), [])
  219. @skipUnlessDBFeature('supports_transactions')
  220. def test_commit_on_success_exit(self):
  221. with transaction.autocommit():
  222. with transaction.commit_on_success():
  223. Reporter.objects.create(first_name="Bobby", last_name="Tables")
  224. # Much more formal
  225. r = Reporter.objects.get()
  226. r.first_name = "Robert"
  227. r.save()
  228. r = Reporter.objects.get()
  229. self.assertEqual(r.first_name, "Robert")
  230. @skipUnlessDBFeature('supports_transactions')
  231. def test_manually_managed(self):
  232. """
  233. You can manually manage transactions if you really want to, but you
  234. have to remember to commit/rollback.
  235. """
  236. with transaction.commit_manually():
  237. Reporter.objects.create(first_name="Libby", last_name="Holtzman")
  238. transaction.commit()
  239. self.assertEqual(Reporter.objects.count(), 1)
  240. @skipUnlessDBFeature('supports_transactions')
  241. def test_manually_managed_mistake(self):
  242. """
  243. If you forget, you'll get bad errors.
  244. """
  245. with self.assertRaises(transaction.TransactionManagementError):
  246. with transaction.commit_manually():
  247. Reporter.objects.create(first_name="Scott", last_name="Browning")
  248. @skipUnlessDBFeature('supports_transactions')
  249. def test_manually_managed_with_using(self):
  250. """
  251. The commit_manually function also works with a using argument.
  252. """
  253. with self.assertRaises(transaction.TransactionManagementError):
  254. with transaction.commit_manually(using="default"):
  255. Reporter.objects.create(first_name="Walter", last_name="Cronkite")
  256. @skipUnlessDBFeature('requires_rollback_on_dirty_transaction')
  257. def test_bad_sql(self):
  258. """
  259. Regression for #11900: If a block wrapped by commit_on_success
  260. writes a transaction that can't be committed, that transaction should
  261. be rolled back. The bug is only visible using the psycopg2 backend,
  262. though the fix is generally a good idea.
  263. """
  264. with self.assertRaises(IntegrityError):
  265. with transaction.commit_on_success():
  266. cursor = connection.cursor()
  267. cursor.execute("INSERT INTO transactions_reporter (first_name, last_name) VALUES ('Douglas', 'Adams');")
  268. transaction.set_dirty()
  269. transaction.rollback()