mirror of
https://github.com/django/django.git
synced 2024-12-22 17:16:24 +00:00
Fixed #32511 -- Corrected handling prefetched nested reverse relationships.
When prefetching a set of child objects related to a set of parent objects, we usually want to populate the relationship back from the child to the parent to avoid a query when accessing that relationship attribute. However, there's an edge case where the child queryset itself specifies a prefetch back to the parent. In that case, we want to use the prefetched relationship rather than populating the reverse relationship from the parent.
This commit is contained in:
parent
973fa56652
commit
f5233dce30
1
AUTHORS
1
AUTHORS
@ -423,6 +423,7 @@ answer newbie questions, and generally made Django that much better:
|
|||||||
James Timmins <jameshtimmins@gmail.com>
|
James Timmins <jameshtimmins@gmail.com>
|
||||||
James Turk <dev@jamesturk.net>
|
James Turk <dev@jamesturk.net>
|
||||||
James Wheare <django@sparemint.com>
|
James Wheare <django@sparemint.com>
|
||||||
|
Jamie Matthews <jamie@mtth.org>
|
||||||
Jannis Leidel <jannis@leidel.info>
|
Jannis Leidel <jannis@leidel.info>
|
||||||
Janos Guljas
|
Janos Guljas
|
||||||
Jan Pazdziora
|
Jan Pazdziora
|
||||||
|
@ -646,8 +646,9 @@ def create_reverse_many_to_one_manager(superclass, rel):
|
|||||||
# Since we just bypassed this class' get_queryset(), we must manage
|
# Since we just bypassed this class' get_queryset(), we must manage
|
||||||
# the reverse relation manually.
|
# the reverse relation manually.
|
||||||
for rel_obj in queryset:
|
for rel_obj in queryset:
|
||||||
instance = instances_dict[rel_obj_attr(rel_obj)]
|
if not self.field.is_cached(rel_obj):
|
||||||
setattr(rel_obj, self.field.name, instance)
|
instance = instances_dict[rel_obj_attr(rel_obj)]
|
||||||
|
setattr(rel_obj, self.field.name, instance)
|
||||||
cache_name = self.field.remote_field.get_cache_name()
|
cache_name = self.field.remote_field.get_cache_name()
|
||||||
return queryset, rel_obj_attr, instance_attr, False, cache_name, False
|
return queryset, rel_obj_attr, instance_attr, False, cache_name, False
|
||||||
|
|
||||||
|
@ -1614,3 +1614,29 @@ class ReadPrefetchedObjectsCacheTests(TestCase):
|
|||||||
with self.assertNumQueries(4):
|
with self.assertNumQueries(4):
|
||||||
# AuthorWithAge -> Author -> FavoriteAuthors, Book
|
# AuthorWithAge -> Author -> FavoriteAuthors, Book
|
||||||
self.assertSequenceEqual(authors, [self.author1, self.author2])
|
self.assertSequenceEqual(authors, [self.author1, self.author2])
|
||||||
|
|
||||||
|
|
||||||
|
class NestedPrefetchTests(TestCase):
|
||||||
|
@classmethod
|
||||||
|
def setUpTestData(cls):
|
||||||
|
house = House.objects.create(name='Big house', address='123 Main St')
|
||||||
|
cls.room = Room.objects.create(name='Kitchen', house=house)
|
||||||
|
|
||||||
|
def test_nested_prefetch_is_not_overwritten_by_related_object(self):
|
||||||
|
"""
|
||||||
|
The prefetched relationship is used rather than populating the reverse
|
||||||
|
relationship from the parent, when prefetching a set of child objects
|
||||||
|
related to a set of parent objects and the child queryset itself
|
||||||
|
specifies a prefetch back to the parent.
|
||||||
|
"""
|
||||||
|
queryset = House.objects.only('name').prefetch_related(
|
||||||
|
Prefetch('rooms', queryset=Room.objects.prefetch_related(
|
||||||
|
Prefetch('house', queryset=House.objects.only('address')),
|
||||||
|
)),
|
||||||
|
)
|
||||||
|
with self.assertNumQueries(3):
|
||||||
|
house = queryset.first()
|
||||||
|
|
||||||
|
self.assertIs(Room.house.is_cached(self.room), True)
|
||||||
|
with self.assertNumQueries(0):
|
||||||
|
house.rooms.first().house.address
|
||||||
|
Loading…
Reference in New Issue
Block a user