summaryrefslogtreecommitdiff
path: root/django/db
diff options
context:
space:
mode:
authorKeryn Knight <keryn@kerynknight.com>2026-01-10 08:39:45 +0100
committerMariusz Felisiak <felisiak.mariusz@gmail.com>2026-03-11 18:05:44 +0100
commit804607df0e174c524a3ea880b8ecbb555ecb4abb (patch)
tree8d602a76d9cdcbec2357a96588e68886bb3bd705 /django/db
parent8d8a8713432a88737c4400610eef11c5c8457b86 (diff)
Refs #28455 -- Avoided QuerySet cloning in simple prefetch_related() usages.
manager.get_queryset() always returns freshly instantiated per-instance QuerySet which doesn't need subsequent cloning. Based on work originally done by Anssi Kääriäinen and Tim Graham.
Diffstat (limited to 'django/db')
-rw-r--r--django/db/models/fields/related_descriptors.py26
-rw-r--r--django/db/models/query.py2
2 files changed, 15 insertions, 13 deletions
diff --git a/django/db/models/fields/related_descriptors.py b/django/db/models/fields/related_descriptors.py
index 40ad8b260f..87b4921a6c 100644
--- a/django/db/models/fields/related_descriptors.py
+++ b/django/db/models/fields/related_descriptors.py
@@ -738,12 +738,13 @@ def create_reverse_many_to_one_manager(superclass, rel):
empty_strings_as_null = connections[
db
].features.interprets_empty_strings_as_nulls
- queryset._add_hints(instance=self.instance)
- if self._db:
- queryset = queryset.using(self._db)
- queryset._fetch_mode = self.instance._state.fetch_mode
- queryset._defer_next_filter = True
- queryset = queryset.filter(**self.core_filters)
+ with queryset._avoid_cloning():
+ queryset._add_hints(instance=self.instance)
+ if self._db:
+ queryset = queryset.using(self._db)
+ queryset._fetch_mode = self.instance._state.fetch_mode
+ queryset._defer_next_filter = True
+ queryset = queryset.filter(**self.core_filters)
for field in self.field.foreign_related_fields:
val = getattr(self.instance, field.attname)
if val is None or (val == "" and empty_strings_as_null):
@@ -1140,12 +1141,13 @@ def create_forward_many_to_many_manager(superclass, rel, reverse):
"""
Filter the queryset for the instance this manager is bound to.
"""
- queryset._add_hints(instance=self.instance)
- if self._db:
- queryset = queryset.using(self._db)
- queryset._fetch_mode = self.instance._state.fetch_mode
- queryset._defer_next_filter = True
- return queryset._next_is_sticky().filter(**self.core_filters)
+ with queryset._avoid_cloning():
+ queryset._add_hints(instance=self.instance)
+ if self._db:
+ queryset = queryset.using(self._db)
+ queryset._fetch_mode = self.instance._state.fetch_mode
+ queryset._defer_next_filter = True
+ return queryset._next_is_sticky().filter(**self.core_filters)
def get_prefetch_cache(self):
# Walk up the ancestor-chain (if cached) to try and find a prefetch
diff --git a/django/db/models/query.py b/django/db/models/query.py
index d4775308b8..dca504e441 100644
--- a/django/db/models/query.py
+++ b/django/db/models/query.py
@@ -2906,7 +2906,7 @@ def prefetch_one_level(instances, prefetcher, lookup, level):
else:
manager = getattr(obj, to_attr)
if leaf and lookup.queryset is not None:
- qs = manager._apply_rel_filters(lookup.queryset)
+ qs = manager._apply_rel_filters(lookup.queryset._chain())
else:
qs = manager.get_queryset()
qs._result_cache = vals