From 1109e669905d2011561e843f0ab7c2c02fb63c4d Mon Sep 17 00:00:00 2001 From: Simon Charette Date: Wed, 4 May 2022 01:31:59 -0500 Subject: [PATCH] Refs #33646 -- Reduced sync_to_async hops on async QuerySet iteration. Until we add support for truly asynchronous database backends it's actually detrimental to have complete set retrieval require multiple hops between sync and async emulated contexts via asgiref. By defaulting to sending the whole sync _fetch_all() to the current context thread pool we reduce unncessary work when dealing with large result sets since the queryset cannot be iterated anyway before all results are retrieved and cached. --- django/db/models/query.py | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/django/db/models/query.py b/django/db/models/query.py index ce7e295d524..67fcb411eb5 100644 --- a/django/db/models/query.py +++ b/django/db/models/query.py @@ -398,7 +398,7 @@ class QuerySet: # Remember, __aiter__ itself is synchronous, it's the thing it returns # that is async! async def generator(): - await self._async_fetch_all() + await sync_to_async(self._fetch_all)() for item in self._result_cache: yield item @@ -1842,12 +1842,6 @@ class QuerySet: if self._prefetch_related_lookups and not self._prefetch_done: self._prefetch_related_objects() - async def _async_fetch_all(self): - if self._result_cache is None: - self._result_cache = [result async for result in self._iterable_class(self)] - if self._prefetch_related_lookups and not self._prefetch_done: - sync_to_async(self._prefetch_related_objects)() - def _next_is_sticky(self): """ Indicate that the next filter call and the one following that should @@ -2025,12 +2019,6 @@ class RawQuerySet: if self._prefetch_related_lookups and not self._prefetch_done: self._prefetch_related_objects() - async def _async_fetch_all(self): - if self._result_cache is None: - self._result_cache = [result async for result in RawModelIterable(self)] - if self._prefetch_related_lookups and not self._prefetch_done: - sync_to_async(self._prefetch_related_objects)() - def __len__(self): self._fetch_all() return len(self._result_cache) @@ -2047,7 +2035,7 @@ class RawQuerySet: # Remember, __aiter__ itself is synchronous, it's the thing it returns # that is async! async def generator(): - await self._async_fetch_all() + await sync_to_async(self._fetch_all)() for item in self._result_cache: yield item