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