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>
This commit is contained in:
Akash Kumar Sen 2023-05-30 20:08:43 +05:30 committed by Mariusz Felisiak
parent 1c4f5f314e
commit 5d20e02078
3 changed files with 49 additions and 11 deletions

View File

@ -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:

View File

@ -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

View File

@ -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