Skip to content

Commit

Permalink
Fixed #34889 -- Fixed get_prefetch_queryset() fallback in prefetch_on…
Browse files Browse the repository at this point in the history
…e_level().

Thanks Matt Westcott for the report.

Regression in cac94dd.
  • Loading branch information
felixxm authored Oct 6, 2023
1 parent 90c75dc commit 296b75a
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 5 deletions.
7 changes: 4 additions & 3 deletions django/db/models/query.py
Original file line number Diff line number Diff line change
Expand Up @@ -2550,16 +2550,17 @@ def prefetch_one_level(instances, prefetcher, lookup, level):
RemovedInDjango60Warning,
stacklevel=2,
)
queryset = None
if querysets := lookup.get_current_querysets(level):
queryset = querysets[0]
(
rel_qs,
rel_obj_attr,
instance_attr,
single,
cache_name,
is_descriptor,
) = prefetcher.get_prefetch_queryset(
instances, lookup.get_current_querysets(level)
)
) = prefetcher.get_prefetch_queryset(instances, queryset)
# We have to handle the possibility that the QuerySet we just got back
# contains some prefetch_related lookups. We don't want to trigger the
# prefetch_related functionality by evaluating the query. Rather, we need
Expand Down
44 changes: 42 additions & 2 deletions tests/prefetch_related/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@
from django.core.exceptions import ObjectDoesNotExist
from django.db import NotSupportedError, connection
from django.db.models import Prefetch, QuerySet, prefetch_related_objects
from django.db.models.query import get_prefetcher
from django.db.models.fields.related import ForwardManyToOneDescriptor
from django.db.models.query import get_prefetcher, prefetch_one_level
from django.db.models.sql import Query
from django.test import (
TestCase,
ignore_warnings,
override_settings,
skipIfDBFeature,
skipUnlessDBFeature,
Expand Down Expand Up @@ -1997,7 +1999,7 @@ def test_window_not_supported(self):
list(Book.objects.prefetch_related(Prefetch("authors", authors[1:])))


class GetCurrentQuerySetDeprecation(TestCase):
class DeprecationTests(TestCase):
def test_get_current_queryset_warning(self):
msg = (
"Prefetch.get_current_queryset() is deprecated. Use "
Expand All @@ -2011,3 +2013,41 @@ def test_get_current_queryset_warning(self):
)
with self.assertWarnsMessage(RemovedInDjango60Warning, msg):
self.assertIsNone(Prefetch("authors").get_current_queryset(1))

@ignore_warnings(category=RemovedInDjango60Warning)
def test_prefetch_one_level_fallback(self):
class NoGetPrefetchQuerySetsDescriptor(ForwardManyToOneDescriptor):
def get_prefetch_queryset(self, instances, queryset=None):
if queryset is None:
return super().get_prefetch_querysets(instances)
return super().get_prefetch_querysets(instances, [queryset])

def __getattribute__(self, name):
if name == "get_prefetch_querysets":
raise AttributeError
return super().__getattribute__(name)

house = House.objects.create()
room = Room.objects.create(house=house)
house.main_room = room
house.save()

# prefetch_one_level() fallbacks to get_prefetch_queryset().
prefetcher = NoGetPrefetchQuerySetsDescriptor(Room._meta.get_field("house"))
obj_list, additional_lookups = prefetch_one_level(
[room],
prefetcher,
Prefetch("house", House.objects.all()),
0,
)
self.assertEqual(obj_list, [house])
self.assertEqual(additional_lookups, [])

obj_list, additional_lookups = prefetch_one_level(
[room],
prefetcher,
Prefetch("house"),
0,
)
self.assertEqual(obj_list, [house])
self.assertEqual(additional_lookups, [])

0 comments on commit 296b75a

Please sign in to comment.