#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.

Reply via email to