#35677: Unexpected behaviour of Prefetch with queryset filtering on a through
model
-------------------------------------+-------------------------------------
Reporter: David Glenck | Owner: (none)
Type: Uncategorized | Status: closed
Component: Database layer | Version: 5.1
(models, ORM) |
Severity: Normal | Resolution: needsinfo
Keywords: Prefetch, | Triage Stage:
prefetch_related, many-to-many | Unreviewed
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Comment (by David Glenck):
Hi Natalia
Thank you for pointing me to some related issues.
I checked them and they refer to some behaviour I also referred to, but
the issue I am pointing out is a different one.
What I am trying to do is to optimize a repeating query of this shape (but
of course more complex in reality)
{{{#!python
for subscriber in subscribers:
subscriber.subscriptions.filter(status__is_active=True)
}}}
by prefetching the related objects like this:
{{{#!python
prefetched =
Subscriber.objects.filter(pk__in=subscribers).prefetch_related(
Prefetch(
'subscriptions',
queryset=Subscription.objects.filter(status__is_active=True),
to_attr='active_subscriptions'
)
)
for subscriber in prefetched:
prefetched[0].active_subscriptions
}}}
From the code above I would expect, that the prefetch gives the same
results as my un-prefetched query. But it doesn't.
This is because the first case, django does some magic internally, to
combine my `.filter(status__is_active=True)`, with the internal filter to
select subscriptions from this specific subscriber. Such that the result
is, as if it would be a single filter (which is relevant in many-to-many
relations).
But in the case of the prefetch django doesn't do that magic internally
and thus it returns a different result.
So my proposal is to make the prefetched result behave the same as the
non-prefetched one by modifying the internal behaviour of the prefetch
query. Or at least give an option to do so. Because currently I have no
clean way to instruct the prefetch to do what I want.
I'm not sure if this is a bug or a feature request, because this specific
case is not mentioned in the documentation, but I would argue that it is
the expected behaviour.
For illustration, this dirty workaround makes the prefetch return the
result, that I would expect (but has undesired side-effects):
{{{#!python
class StickyQueryset(models.QuerySet):
def _chain(self):
self._sticky_filter = True
return self
class Subscription(models.Model):
provider_name = models.CharField(max_length=255)
objects = StickyQueryset.as_manager()
}}}
I tried looking deeper into the QuerySet code to see if I can propose some
patch. But it's rather complex code in there.
--
Ticket URL: <https://code.djangoproject.com/ticket/35677#comment:2>
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/01070191529ed490-1a6a7289-f740-4196-b43f-ffb9fba3f30d-000000%40eu-central-1.amazonses.com.