tests.py 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. from django.core.exceptions import ObjectNotUpdated
  2. from django.db import DatabaseError, IntegrityError, models, transaction
  3. from django.test import TestCase
  4. from .models import (
  5. Counter,
  6. DiamondSubSubCounter,
  7. InheritedCounter,
  8. OtherSubCounter,
  9. ProxyCounter,
  10. SubCounter,
  11. SubSubCounter,
  12. WithCustomPK,
  13. )
  14. class ForceTests(TestCase):
  15. def test_force_update(self):
  16. c = Counter.objects.create(name="one", value=1)
  17. # The normal case
  18. c.value = 2
  19. c.save()
  20. # Same thing, via an update
  21. c.value = 3
  22. c.save(force_update=True)
  23. # Won't work because force_update and force_insert are mutually
  24. # exclusive
  25. c.value = 4
  26. msg = "Cannot force both insert and updating in model saving."
  27. with self.assertRaisesMessage(ValueError, msg):
  28. c.save(force_insert=True, force_update=True)
  29. # Try to update something that doesn't have a primary key in the first
  30. # place.
  31. c1 = Counter(name="two", value=2)
  32. msg = "Cannot force an update in save() with no primary key."
  33. with self.assertRaisesMessage(ValueError, msg):
  34. with transaction.atomic():
  35. c1.save(force_update=True)
  36. c1.save(force_insert=True)
  37. # Won't work because we can't insert a pk of the same value.
  38. c.value = 5
  39. with self.assertRaises(IntegrityError):
  40. with transaction.atomic():
  41. c.save(force_insert=True)
  42. # Trying to update should still fail, even with manual primary keys, if
  43. # the data isn't in the database already.
  44. obj = WithCustomPK(name=1, value=1)
  45. msg = "Forced update did not affect any rows."
  46. # Make sure backward compatibility with DatabaseError is preserved.
  47. exceptions = [DatabaseError, ObjectNotUpdated, WithCustomPK.NotUpdated]
  48. for exception in exceptions:
  49. with (
  50. self.subTest(exception),
  51. self.assertRaisesMessage(DatabaseError, msg),
  52. transaction.atomic(),
  53. ):
  54. obj.save(force_update=True)
  55. class InheritanceTests(TestCase):
  56. def test_force_update_on_inherited_model(self):
  57. a = InheritedCounter(name="count", value=1, tag="spam")
  58. a.save()
  59. a.save(force_update=True)
  60. def test_force_update_on_proxy_model(self):
  61. a = ProxyCounter(name="count", value=1)
  62. a.save()
  63. a.save(force_update=True)
  64. def test_force_update_on_inherited_model_without_fields(self):
  65. """
  66. Issue 13864: force_update fails on subclassed models, if they don't
  67. specify custom fields.
  68. """
  69. a = SubCounter(name="count", value=1)
  70. a.save()
  71. a.value = 2
  72. a.save(force_update=True)
  73. class ForceInsertInheritanceTests(TestCase):
  74. def test_force_insert_not_bool_or_tuple(self):
  75. msg = "force_insert must be a bool or tuple."
  76. with self.assertRaisesMessage(TypeError, msg), transaction.atomic():
  77. Counter().save(force_insert=1)
  78. with self.assertRaisesMessage(TypeError, msg), transaction.atomic():
  79. Counter().save(force_insert="test")
  80. with self.assertRaisesMessage(TypeError, msg), transaction.atomic():
  81. Counter().save(force_insert=[])
  82. def test_force_insert_not_model(self):
  83. msg = f"Invalid force_insert member. {object!r} must be a model subclass."
  84. with self.assertRaisesMessage(TypeError, msg), transaction.atomic():
  85. Counter().save(force_insert=(object,))
  86. instance = Counter()
  87. msg = f"Invalid force_insert member. {instance!r} must be a model subclass."
  88. with self.assertRaisesMessage(TypeError, msg), transaction.atomic():
  89. Counter().save(force_insert=(instance,))
  90. def test_force_insert_not_base(self):
  91. msg = "Invalid force_insert member. SubCounter must be a base of Counter."
  92. with self.assertRaisesMessage(TypeError, msg):
  93. Counter().save(force_insert=(SubCounter,))
  94. def test_force_insert_false(self):
  95. with self.assertNumQueries(3):
  96. obj = SubCounter.objects.create(pk=1, value=0)
  97. with self.assertNumQueries(2):
  98. SubCounter(pk=obj.pk, value=1).save()
  99. obj.refresh_from_db()
  100. self.assertEqual(obj.value, 1)
  101. with self.assertNumQueries(2):
  102. SubCounter(pk=obj.pk, value=2).save(force_insert=False)
  103. obj.refresh_from_db()
  104. self.assertEqual(obj.value, 2)
  105. with self.assertNumQueries(2):
  106. SubCounter(pk=obj.pk, value=3).save(force_insert=())
  107. obj.refresh_from_db()
  108. self.assertEqual(obj.value, 3)
  109. def test_force_insert_false_with_existing_parent(self):
  110. parent = Counter.objects.create(pk=1, value=1)
  111. with self.assertNumQueries(2):
  112. SubCounter.objects.create(pk=parent.pk, value=2)
  113. def test_force_insert_parent(self):
  114. with self.assertNumQueries(3):
  115. SubCounter(pk=1, value=1).save(force_insert=True)
  116. # Force insert a new parent and don't UPDATE first.
  117. with self.assertNumQueries(2):
  118. SubCounter(pk=2, value=1).save(force_insert=(Counter,))
  119. with self.assertNumQueries(2):
  120. SubCounter(pk=3, value=1).save(force_insert=(models.Model,))
  121. def test_force_insert_with_grandparent(self):
  122. with self.assertNumQueries(4):
  123. SubSubCounter(pk=1, value=1).save(force_insert=True)
  124. # Force insert parents on all levels and don't UPDATE first.
  125. with self.assertNumQueries(3):
  126. SubSubCounter(pk=2, value=1).save(force_insert=(models.Model,))
  127. with self.assertNumQueries(3):
  128. SubSubCounter(pk=3, value=1).save(force_insert=(Counter,))
  129. # Force insert only the last parent.
  130. with self.assertNumQueries(4):
  131. SubSubCounter(pk=4, value=1).save(force_insert=(SubCounter,))
  132. def test_force_insert_with_existing_grandparent(self):
  133. # Force insert only the last child.
  134. grandparent = Counter.objects.create(pk=1, value=1)
  135. with self.assertNumQueries(4):
  136. SubSubCounter(pk=grandparent.pk, value=1).save(force_insert=True)
  137. # Force insert a parent, and don't force insert a grandparent.
  138. grandparent = Counter.objects.create(pk=2, value=1)
  139. with self.assertNumQueries(3):
  140. SubSubCounter(pk=grandparent.pk, value=1).save(force_insert=(SubCounter,))
  141. # Force insert parents on all levels, grandparent conflicts.
  142. grandparent = Counter.objects.create(pk=3, value=1)
  143. with self.assertRaises(IntegrityError), transaction.atomic():
  144. SubSubCounter(pk=grandparent.pk, value=1).save(force_insert=(Counter,))
  145. def test_force_insert_diamond_mti(self):
  146. # Force insert all parents.
  147. with self.assertNumQueries(4):
  148. DiamondSubSubCounter(pk=1, value=1).save(
  149. force_insert=(Counter, SubCounter, OtherSubCounter)
  150. )
  151. with self.assertNumQueries(4):
  152. DiamondSubSubCounter(pk=2, value=1).save(force_insert=(models.Model,))
  153. # Force insert parents, and don't force insert a common grandparent.
  154. with self.assertNumQueries(5):
  155. DiamondSubSubCounter(pk=3, value=1).save(
  156. force_insert=(SubCounter, OtherSubCounter)
  157. )
  158. grandparent = Counter.objects.create(pk=4, value=1)
  159. with self.assertNumQueries(4):
  160. DiamondSubSubCounter(pk=grandparent.pk, value=1).save(
  161. force_insert=(SubCounter, OtherSubCounter),
  162. )
  163. # Force insert all parents, grandparent conflicts.
  164. grandparent = Counter.objects.create(pk=5, value=1)
  165. with self.assertRaises(IntegrityError), transaction.atomic():
  166. DiamondSubSubCounter(pk=grandparent.pk, value=1).save(
  167. force_insert=(models.Model,)
  168. )