mirror of https://github.com/django/django.git
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:
parent
1c4f5f314e
commit
5d20e02078
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue