#35677: Unexpected behaviour of Prefetch with queryset filtering on a through
model
-------------------------------------+-------------------------------------
Reporter: David Glenck | Owner: (none)
Type: Bug | Status: new
Component: Database layer | Version: 5.1
(models, ORM) |
Severity: Normal | Resolution:
Keywords: Prefetch, | Triage Stage: Accepted
prefetch_related, many-to-many |
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Changes (by Simon Charette):
* resolution: needsinfo =>
* stage: Unreviewed => Accepted
* status: closed => new
Comment:
I think the following is what you are requesting. I'm re-opening Natalia
as I believe this is a legitimate bug.
{{{#!diff
diff --git a/django/db/models/fields/related_descriptors.py
b/django/db/models/fields/related_descriptors.py
index bc288c47ec..5356e28d22 100644
--- a/django/db/models/fields/related_descriptors.py
+++ b/django/db/models/fields/related_descriptors.py
@@ -94,9 +94,9 @@ def __set__(self, instance, value):
instance.__dict__[self.field.attname] = value
-def _filter_prefetch_queryset(queryset, field_name, instances):
+def _filter_prefetch_queryset(queryset, field_name, instances, db):
predicate = Q(**{f"{field_name}__in": instances})
- db = queryset._db or DEFAULT_DB_ALIAS
+ db = db or DEFAULT_DB_ALIAS
if queryset.query.is_sliced:
if not connections[db].features.supports_over_clause:
raise NotSupportedError(
@@ -785,12 +785,15 @@ def get_prefetch_querysets(self, instances,
querysets=None):
)
queryset = querysets[0] if querysets else
super().get_queryset()
queryset._add_hints(instance=instances[0])
- queryset = queryset.using(queryset._db or self._db)
+ db = queryset._db or self._db
+ queryset = queryset.using(db)
rel_obj_attr = self.field.get_local_related_value
instance_attr = self.field.get_foreign_related_value
instances_dict = {instance_attr(inst): inst for inst in
instances}
- queryset = _filter_prefetch_queryset(queryset,
self.field.name, instances)
+ queryset = _filter_prefetch_queryset(
+ queryset, self.field.name, instances, db
+ )
# Since we just bypassed this class' get_queryset(), we must
manage
# the reverse relation manually.
@@ -1165,10 +1168,15 @@ def get_prefetch_querysets(self, instances,
querysets=None):
)
queryset = querysets[0] if querysets else
super().get_queryset()
queryset._add_hints(instance=instances[0])
- queryset = queryset.using(queryset._db or self._db)
+ db = queryset._db or self._db
+ # Make sure filters are applied in a "sticky" fashion to
reuse
+ # multi-valued relationships like direct filter() calls
against
+ # many-to-many managers do.
+ queryset.query.filter_is_sticky = True
queryset = _filter_prefetch_queryset(
- queryset._next_is_sticky(), self.query_field_name,
instances
+ queryset, self.query_field_name, instances, db
)
+ queryset = queryset.using(db)
# M2M: need to annotate the query in order to get the primary
model
# that the secondary model was actually related to. We know
that
}}}
David, if you'd up to it it seems that all this bugfix is missing are
tests that can be added to the `prefetch_related` test app if you're
interested in submitting a PR once Github is back online.
--
Ticket URL: <https://code.djangoproject.com/ticket/35677#comment:5>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.
--
You received this message because you are subscribed to the Google Groups
"Django updates" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To view this discussion on the web visit
https://groups.google.com/d/msgid/django-updates/01070191536e2e10-fd6b182e-ec2f-4609-a595-498e4ee19bda-000000%40eu-central-1.amazonses.com.