mirror of https://github.com/django/django.git
Refs #34634 -- Fixed creating diamond-shaped MTI objects with ancestors inherited from different paths.
Co-authored-by: Simon Charette <charette.s@gmail.com>
This commit is contained in:
parent
82a588a6bc
commit
1754c2c802
|
@ -864,7 +864,7 @@ class Options:
|
|||
reverse=True,
|
||||
include_parents=True,
|
||||
include_hidden=False,
|
||||
seen_models=None,
|
||||
topmost_call=True,
|
||||
):
|
||||
"""
|
||||
Internal helper function to return fields of the model.
|
||||
|
@ -885,13 +885,6 @@ class Options:
|
|||
# implementation and to provide a fast way for Django's internals to
|
||||
# access specific subsets of fields.
|
||||
|
||||
# We must keep track of which models we have already seen. Otherwise we
|
||||
# could include the same field multiple times from different models.
|
||||
topmost_call = seen_models is None
|
||||
if topmost_call:
|
||||
seen_models = set()
|
||||
seen_models.add(self.model)
|
||||
|
||||
# Creates a cache key composed of all arguments
|
||||
cache_key = (forward, reverse, include_parents, include_hidden, topmost_call)
|
||||
|
||||
|
@ -906,12 +899,11 @@ class Options:
|
|||
# Recursively call _get_fields() on each parent, with the same
|
||||
# options provided in this call.
|
||||
if include_parents is not False:
|
||||
# In diamond inheritance it is possible that we see the same model
|
||||
# from two different routes. In that case, avoid adding fields from
|
||||
# the same parent again.
|
||||
parent_fields = set()
|
||||
for parent in self.parents:
|
||||
# In diamond inheritance it is possible that we see the same
|
||||
# model from two different routes. In that case, avoid adding
|
||||
# fields from the same parent again.
|
||||
if parent in seen_models:
|
||||
continue
|
||||
if (
|
||||
parent._meta.concrete_model != self.concrete_model
|
||||
and include_parents == PROXY_PARENTS
|
||||
|
@ -922,13 +914,15 @@ class Options:
|
|||
reverse=reverse,
|
||||
include_parents=include_parents,
|
||||
include_hidden=include_hidden,
|
||||
seen_models=seen_models,
|
||||
topmost_call=False,
|
||||
):
|
||||
if (
|
||||
not getattr(obj, "parent_link", False)
|
||||
or obj.model == self.concrete_model
|
||||
):
|
||||
) and obj not in parent_fields:
|
||||
fields.append(obj)
|
||||
parent_fields.add(obj)
|
||||
|
||||
if reverse and not self.proxy:
|
||||
# Tree is computed once and cached until the app cache is expired.
|
||||
# It is composed of a list of fields pointing to the current model
|
||||
|
|
|
@ -106,6 +106,12 @@ class ItalianRestaurant(Restaurant):
|
|||
serves_gnocchi = models.BooleanField(default=False)
|
||||
|
||||
|
||||
class ItalianRestaurantCommonParent(ItalianRestaurant, Place):
|
||||
place_ptr_two = models.OneToOneField(
|
||||
Place, on_delete=models.CASCADE, parent_link=True
|
||||
)
|
||||
|
||||
|
||||
class Supplier(Place):
|
||||
customers = models.ManyToManyField(Restaurant, related_name="provider")
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@ from .models import (
|
|||
GrandChild,
|
||||
GrandParent,
|
||||
ItalianRestaurant,
|
||||
ItalianRestaurantCommonParent,
|
||||
MixinModel,
|
||||
Parent,
|
||||
ParkingLot,
|
||||
|
@ -158,6 +159,28 @@ class ModelInheritanceTests(TestCase):
|
|||
with self.assertNumQueries(4):
|
||||
common_child.save()
|
||||
|
||||
def test_create_diamond_mti_common_parent(self):
|
||||
with self.assertNumQueries(4):
|
||||
italian_restaurant_child = ItalianRestaurantCommonParent.objects.create(
|
||||
name="Ristorante Miron",
|
||||
address="1234 W. Ash",
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
italian_restaurant_child.italianrestaurant_ptr.place_ptr,
|
||||
italian_restaurant_child.place_ptr_two,
|
||||
)
|
||||
self.assertEqual(
|
||||
italian_restaurant_child.italianrestaurant_ptr.restaurant_ptr,
|
||||
italian_restaurant_child.restaurant_ptr,
|
||||
)
|
||||
self.assertEqual(
|
||||
italian_restaurant_child.restaurant_ptr.place_ptr,
|
||||
italian_restaurant_child.place_ptr_two,
|
||||
)
|
||||
self.assertEqual(italian_restaurant_child.name, "Ristorante Miron")
|
||||
self.assertEqual(italian_restaurant_child.address, "1234 W. Ash")
|
||||
|
||||
def test_update_parent_filtering(self):
|
||||
"""
|
||||
Updating a field of a model subclass doesn't issue an UPDATE
|
||||
|
|
Loading…
Reference in New Issue