#25544: prefetch_related sends duplicate ids to database
----------------------------------------------+----------------------------
     Reporter:  spectras                      |      Owner:  nobody
         Type:  Uncategorized                 |     Status:  new
    Component:  Database layer (models, ORM)  |    Version:  1.8
     Severity:  Normal                        |   Keywords:  prefetch
                                              |  duplicate
 Triage Stage:  Unreviewed                    |  Has patch:  0
Easy pickings:  0                             |      UI/UX:  0
----------------------------------------------+----------------------------
 That may be intended, but it seems dubious, so…

 Issue happens when prefetching a model relation from another one, linked
 by a M2M field. Sample case:

 {{{#!python
 class Picture(Model):
     # some fields

 class Collection(Model):
     pictures = ManyToManyField(Picture, blank=True,
                                related_name='collections')
     # some fields

 class CollectionPermission(Model):
     content_object = ForeignKey(Collection,
                                 related_name='permissions')
     # fk to users and fk to permissions
 }}}

 When prefetching collection permissions from a picture queryset, like
 this:

 {{{#!python
 Picture.objects.prefetch_related('collections__permissions')
 }}}

 The third query (the second level of prefetching) looks like this:
 {{{#!sql
 SELECT "gallery_collectionpermission"."id",
        "gallery_collectionpermission"."user_id",
        "gallery_collectionpermission"."permission_id",
        "gallery_collectionpermission"."content_object_id"
 FROM   "gallery_collectionpermission"
 WHERE  "gallery_collectionpermission"."content_object_id" IN (
              11, 11, 11, 11,
              11, 11, 11, 11,
              11, 11, 10, 10,
              10, 10, 10, 10,
              10, 10, 10, 10,
              10, 10, 10, 10,
              10, 10, 10, 10,
              10, 10, 10, 10,
              10, 10, 9, 9,
              9, 9, 9 )
 }}}

 The issue arises in two steps:
 * As prefetch has no identity mapping, the first prefetching step will
 build a different Collection instance for every picture that uses it.
 * Then the second prefetching step will gather all instances, and dump
 their ids to the database without looking for duplicates.

 Ideally, this could be fixed at the first step: when a queryset prefetches
 the same row several times, it would make sense to return a single
 instance. In this example, all Pictures' cache should point to the same
 Gallery instances.

 The second step could also be fixed: the related manager could eliminate
 duplicates when building the query. For some reason the
 ReverseSingleRelatedObjectDescriptor does it, but none of the other
 descriptors or managers do.

 The cost seems huge. Typically, I have collections of hundreds of
 pictures, but each picture will only appear in 4 or 5 collections. The
 query thus sends the db server tens of thousands of ids instead of a few
 dozens.

 Is there something I missed? Or is this use case out of the normal scope
 of `prefetch_related`?

--
Ticket URL: <https://code.djangoproject.com/ticket/25544>
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 post to this group, send email to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-updates/051.8445c4ac9379dc421055626faf72e28c%40djangoproject.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to