Fixed #25546 -- Prevented duplicate queries with nested prefetch_related().
This commit is contained in:
parent
13023ba867
commit
bdbe50a491
|
@ -1521,6 +1521,7 @@ def get_prefetcher(instance, attr):
|
||||||
rel_obj = getattr(instance, attr)
|
rel_obj = getattr(instance, attr)
|
||||||
if hasattr(rel_obj, 'get_prefetch_queryset'):
|
if hasattr(rel_obj, 'get_prefetch_queryset'):
|
||||||
prefetcher = rel_obj
|
prefetcher = rel_obj
|
||||||
|
is_fetched = attr in instance._prefetched_objects_cache
|
||||||
return prefetcher, rel_obj_descriptor, attr_found, is_fetched
|
return prefetcher, rel_obj_descriptor, attr_found, is_fetched
|
||||||
|
|
||||||
|
|
||||||
|
@ -1597,6 +1598,7 @@ def prefetch_one_level(instances, prefetcher, lookup, level):
|
||||||
else:
|
else:
|
||||||
if as_attr:
|
if as_attr:
|
||||||
setattr(obj, to_attr, vals)
|
setattr(obj, to_attr, vals)
|
||||||
|
obj._prefetched_objects_cache[cache_name] = vals
|
||||||
else:
|
else:
|
||||||
# Cache in the QuerySet.all().
|
# Cache in the QuerySet.all().
|
||||||
qs = getattr(obj, to_attr).all()
|
qs = getattr(obj, to_attr).all()
|
||||||
|
|
|
@ -1241,3 +1241,82 @@ class Ticket21760Tests(TestCase):
|
||||||
prefetcher = get_prefetcher(self.rooms[0], 'house')[0]
|
prefetcher = get_prefetcher(self.rooms[0], 'house')[0]
|
||||||
queryset = prefetcher.get_prefetch_queryset(list(Room.objects.all()))[0]
|
queryset = prefetcher.get_prefetch_queryset(list(Room.objects.all()))[0]
|
||||||
self.assertNotIn(' JOIN ', force_text(queryset.query))
|
self.assertNotIn(' JOIN ', force_text(queryset.query))
|
||||||
|
|
||||||
|
|
||||||
|
class Ticket25546Tests(TestCase):
|
||||||
|
"""
|
||||||
|
Nested prefetch_related() shouldn't trigger duplicate queries for the same
|
||||||
|
lookup.
|
||||||
|
|
||||||
|
Before, prefetch queries were for 'addresses', 'first_time_authors', and
|
||||||
|
'first_time_authors__addresses'. The last query is the duplicate.
|
||||||
|
"""
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setUpTestData(cls):
|
||||||
|
cls.book1, cls.book2 = [
|
||||||
|
Book.objects.create(title='book1'),
|
||||||
|
Book.objects.create(title='book2'),
|
||||||
|
]
|
||||||
|
cls.author11, cls.author12, cls.author21 = [
|
||||||
|
Author.objects.create(first_book=cls.book1, name='Author11'),
|
||||||
|
Author.objects.create(first_book=cls.book1, name='Author12'),
|
||||||
|
Author.objects.create(first_book=cls.book2, name='Author21'),
|
||||||
|
]
|
||||||
|
cls.author1_address1, cls.author1_address2, cls.author2_address1 = [
|
||||||
|
AuthorAddress.objects.create(author=cls.author11, address='Happy place'),
|
||||||
|
AuthorAddress.objects.create(author=cls.author12, address='Haunted house'),
|
||||||
|
AuthorAddress.objects.create(author=cls.author21, address='Happy place'),
|
||||||
|
]
|
||||||
|
|
||||||
|
def test_prefetch(self):
|
||||||
|
with self.assertNumQueries(3):
|
||||||
|
books = Book.objects.filter(
|
||||||
|
title__in=['book1', 'book2'],
|
||||||
|
).prefetch_related(
|
||||||
|
Prefetch(
|
||||||
|
'first_time_authors',
|
||||||
|
Author.objects.prefetch_related(
|
||||||
|
Prefetch(
|
||||||
|
'addresses',
|
||||||
|
AuthorAddress.objects.filter(address='Happy place'),
|
||||||
|
)
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
book1, book2 = list(books)
|
||||||
|
|
||||||
|
with self.assertNumQueries(0):
|
||||||
|
self.assertListEqual(list(book1.first_time_authors.all()), [self.author11, self.author12])
|
||||||
|
self.assertListEqual(list(book2.first_time_authors.all()), [self.author21])
|
||||||
|
|
||||||
|
self.assertListEqual(list(book1.first_time_authors.all()[0].addresses.all()), [self.author1_address1])
|
||||||
|
self.assertListEqual(list(book1.first_time_authors.all()[1].addresses.all()), [])
|
||||||
|
self.assertListEqual(list(book2.first_time_authors.all()[0].addresses.all()), [self.author2_address1])
|
||||||
|
|
||||||
|
def test_prefetch_with_to_attr(self):
|
||||||
|
with self.assertNumQueries(3):
|
||||||
|
books = Book.objects.filter(
|
||||||
|
title__in=['book1', 'book2'],
|
||||||
|
).prefetch_related(
|
||||||
|
Prefetch(
|
||||||
|
'first_time_authors',
|
||||||
|
Author.objects.prefetch_related(
|
||||||
|
Prefetch(
|
||||||
|
'addresses',
|
||||||
|
AuthorAddress.objects.filter(address='Happy place'),
|
||||||
|
to_attr='happy_place',
|
||||||
|
)
|
||||||
|
),
|
||||||
|
to_attr='first_authors',
|
||||||
|
),
|
||||||
|
)
|
||||||
|
book1, book2 = list(books)
|
||||||
|
|
||||||
|
with self.assertNumQueries(0):
|
||||||
|
self.assertListEqual(book1.first_authors, [self.author11, self.author12])
|
||||||
|
self.assertListEqual(book2.first_authors, [self.author21])
|
||||||
|
|
||||||
|
self.assertListEqual(book1.first_authors[0].happy_place, [self.author1_address1])
|
||||||
|
self.assertListEqual(book1.first_authors[1].happy_place, [])
|
||||||
|
self.assertListEqual(book2.first_authors[0].happy_place, [self.author2_address1])
|
||||||
|
|
Loading…
Reference in New Issue