summaryrefslogtreecommitdiff
path: root/tests/force_insert_update
diff options
context:
space:
mode:
authorAkash Kumar Sen <Akash-Kumar-Sen@users.noreply.github.com>2023-06-22 18:23:11 +0530
committerMariusz Felisiak <felisiak.mariusz@gmail.com>2023-06-29 21:52:52 +0200
commita40b0103bccb8216c944188d329d8ea5eceb7e92 (patch)
treea08a0def2a0d448515f6b40409ab09e6fc129c0f /tests/force_insert_update
parent601ffb0da3a8c3900164b356c585650c00d238b0 (diff)
Fixed #30382 -- Allowed specifying parent classes in force_insert of Model.save().
Diffstat (limited to 'tests/force_insert_update')
-rw-r--r--tests/force_insert_update/models.py10
-rw-r--r--tests/force_insert_update/tests.py77
2 files changed, 86 insertions, 1 deletions
diff --git a/tests/force_insert_update/models.py b/tests/force_insert_update/models.py
index 586be12f13..b95b197454 100644
--- a/tests/force_insert_update/models.py
+++ b/tests/force_insert_update/models.py
@@ -30,3 +30,13 @@ class SubSubCounter(SubCounter):
class WithCustomPK(models.Model):
name = models.IntegerField(primary_key=True)
value = models.IntegerField()
+
+
+class OtherSubCounter(Counter):
+ other_counter_ptr = models.OneToOneField(
+ Counter, primary_key=True, parent_link=True, on_delete=models.CASCADE
+ )
+
+
+class DiamondSubSubCounter(SubCounter, OtherSubCounter):
+ pass
diff --git a/tests/force_insert_update/tests.py b/tests/force_insert_update/tests.py
index e2fefd3269..cc223cf3ea 100644
--- a/tests/force_insert_update/tests.py
+++ b/tests/force_insert_update/tests.py
@@ -1,9 +1,11 @@
-from django.db import DatabaseError, IntegrityError, transaction
+from django.db import DatabaseError, IntegrityError, models, transaction
from django.test import TestCase
from .models import (
Counter,
+ DiamondSubSubCounter,
InheritedCounter,
+ OtherSubCounter,
ProxyCounter,
SubCounter,
SubSubCounter,
@@ -76,6 +78,29 @@ class InheritanceTests(TestCase):
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)
@@ -87,6 +112,10 @@ class ForceInsertInheritanceTests(TestCase):
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)
@@ -96,13 +125,59 @@ class ForceInsertInheritanceTests(TestCase):
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,)
+ )