summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAkash Kumar Sen <Akash-Kumar-Sen@users.noreply.github.com>2023-05-30 20:08:43 +0530
committerMariusz Felisiak <felisiak.mariusz@gmail.com>2023-06-15 08:16:11 +0200
commit5d20e0207867615f6989714261ea3c0a3eb85d88 (patch)
tree7575ea39f84a1af5c8643e855459c40b8a88f49e
parent1c4f5f314e2b0c77b3fa0c75f703218e7e06f4be (diff)
Fixed #33414 -- Fixed creating diamond-shaped MTI objects for common ancestor with primary key that has a default.
Co-authored-by: Simon Charette <charette.s@gmail.com>
-rw-r--r--django/db/models/base.py31
-rw-r--r--tests/model_inheritance/models.py20
-rw-r--r--tests/model_inheritance/tests.py9
3 files changed, 49 insertions, 11 deletions
diff --git a/django/db/models/base.py b/django/db/models/base.py
index 7aabe0b667..959b72c93b 100644
--- a/django/db/models/base.py
+++ b/django/db/models/base.py
@@ -900,10 +900,12 @@ class Model(AltersData, metaclass=ModelBase):
save_base.alters_data = True
- def _save_parents(self, cls, using, update_fields):
+ def _save_parents(self, cls, using, update_fields, updated_parents=None):
"""Save all the parents of cls using values from self."""
meta = cls._meta
inserted = False
+ if updated_parents is None:
+ updated_parents = {}
for parent, field in meta.parents.items():
# Make sure the link fields are synced between parent and self.
if (
@@ -912,16 +914,23 @@ class Model(AltersData, metaclass=ModelBase):
and getattr(self, field.attname) is not None
):
setattr(self, parent._meta.pk.attname, getattr(self, field.attname))
- parent_inserted = self._save_parents(
- cls=parent, using=using, update_fields=update_fields
- )
- updated = self._save_table(
- cls=parent,
- using=using,
- update_fields=update_fields,
- force_insert=parent_inserted,
- )
- if not updated:
+ if (parent_updated := updated_parents.get(parent)) is None:
+ parent_inserted = self._save_parents(
+ cls=parent,
+ using=using,
+ update_fields=update_fields,
+ updated_parents=updated_parents,
+ )
+ updated = self._save_table(
+ cls=parent,
+ using=using,
+ update_fields=update_fields,
+ force_insert=parent_inserted,
+ )
+ if not updated:
+ inserted = True
+ updated_parents[parent] = updated
+ elif not parent_updated:
inserted = True
# Set the parent's PK value to self.
if field:
diff --git a/tests/model_inheritance/models.py b/tests/model_inheritance/models.py
index dc0e238f7e..aabfdfc03d 100644
--- a/tests/model_inheritance/models.py
+++ b/tests/model_inheritance/models.py
@@ -186,3 +186,23 @@ class Child(Parent):
class GrandChild(Child):
pass
+
+
+class CommonAncestor(models.Model):
+ id = models.IntegerField(primary_key=True, default=1)
+
+
+class FirstParent(CommonAncestor):
+ first_ancestor = models.OneToOneField(
+ CommonAncestor, models.CASCADE, primary_key=True, parent_link=True
+ )
+
+
+class SecondParent(CommonAncestor):
+ second_ancestor = models.OneToOneField(
+ CommonAncestor, models.CASCADE, primary_key=True, parent_link=True
+ )
+
+
+class CommonChild(FirstParent, SecondParent):
+ pass
diff --git a/tests/model_inheritance/tests.py b/tests/model_inheritance/tests.py
index 4542e6c3cc..f1ecacbe29 100644
--- a/tests/model_inheritance/tests.py
+++ b/tests/model_inheritance/tests.py
@@ -9,6 +9,7 @@ from django.test.utils import CaptureQueriesContext, isolate_apps
from .models import (
Base,
Chef,
+ CommonChild,
CommonInfo,
CustomSupplier,
GrandChild,
@@ -149,6 +150,14 @@ class ModelInheritanceTests(TestCase):
# accidentally found).
self.assertSequenceEqual(s.titles.all(), [])
+ def test_create_diamond_mti_default_pk(self):
+ # 1 INSERT for each base.
+ with self.assertNumQueries(4):
+ common_child = CommonChild.objects.create()
+ # 3 SELECTs for the parents, 1 UPDATE for the child.
+ with self.assertNumQueries(4):
+ common_child.save()
+
def test_update_parent_filtering(self):
"""
Updating a field of a model subclass doesn't issue an UPDATE