#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 django-updates+unsubscr...@googlegroups.com.
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