diff options
| author | Akash Kumar Sen <Akash-Kumar-Sen@users.noreply.github.com> | 2023-06-22 18:23:11 +0530 |
|---|---|---|
| committer | Mariusz Felisiak <felisiak.mariusz@gmail.com> | 2023-06-29 21:52:52 +0200 |
| commit | a40b0103bccb8216c944188d329d8ea5eceb7e92 (patch) | |
| tree | a08a0def2a0d448515f6b40409ab09e6fc129c0f /tests | |
| parent | 601ffb0da3a8c3900164b356c585650c00d238b0 (diff) | |
Fixed #30382 -- Allowed specifying parent classes in force_insert of Model.save().
Diffstat (limited to 'tests')
| -rw-r--r-- | tests/extra_regress/models.py | 2 | ||||
| -rw-r--r-- | tests/force_insert_update/models.py | 10 | ||||
| -rw-r--r-- | tests/force_insert_update/tests.py | 77 |
3 files changed, 87 insertions, 2 deletions
diff --git a/tests/extra_regress/models.py b/tests/extra_regress/models.py index 2122bae7ae..4111d439b5 100644 --- a/tests/extra_regress/models.py +++ b/tests/extra_regress/models.py @@ -10,7 +10,7 @@ class RevisionableModel(models.Model): title = models.CharField(blank=True, max_length=255) when = models.DateTimeField(default=datetime.datetime.now) - def save(self, *args, force_insert=None, force_update=None, **kwargs): + def save(self, *args, force_insert=False, force_update=False, **kwargs): super().save( *args, force_insert=force_insert, force_update=force_update, **kwargs ) 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,) + ) |
