#35317: Add the possibility to do prefetches for only a subset of instances
-------------------------------------+-------------------------------------
     Reporter:  Laurent Lyaudet      |                    Owner:  nobody
         Type:  New feature          |                   Status:  closed
    Component:  Database layer       |                  Version:  5.0
  (models, ORM)                      |
     Severity:  Normal               |               Resolution:  wontfix
     Keywords:                       |             Triage Stage:
                                     |  Unreviewed
    Has patch:  1                    |      Needs documentation:  0
  Needs tests:  0                    |  Patch needs improvement:  0
Easy pickings:  0                    |                    UI/UX:  0
-------------------------------------+-------------------------------------
Comment (by Laurent Lyaudet):

 I did a second PR https://github.com/django/django/pull/18003 adding a
 post_prefetch_callback argument to Prefetch.
 Here is a redacted production code example that I could develop with the
 corresponding patch in my package django-monkey-patches.
 I doubt you can find an efficient way and requiring around the same amount
 of code
 to do the same thing without these features.

 {{{#!python
 class OModelSerializer(...):
     @staticmethod
     def enhance_queryset(
         query_set,
         prefix: str = "",
         c_id: Optional[CId] = None,
     ):
         query_set = SomeMixin1.enhance_queryset(query_set, prefix)

         def ventilate_ps_by_c_id(
             lookup,
             done_queries,
         ):
             prefetch_to = lookup.prefetch_to
             prefetch_before = get_previous_prefetch_to(prefetch_to)
             for obj in prefetch_before:
                 if hasattr(obj, "needed_ps"):
                     if not hasattr(obj, "needed_p_by"):
                         obj.needed_p_by = {}
                     for obj2 in obj.needed_ps:
                         obj.needed_p_by[obj2.c_id] = obj2

         return query_set.prefetch_related(
             f"{prefix}p2",
             Prefetch(
                 f"{prefix}s",
 post_prefetch_callback=create_post_prefetch_callback_add_backward_multiple(
                     retrieve_forward_cache_callback=lambda o: [o.s] if
 o.s_id else [],
                     backward_cache_name="current_o_ancestors",
                 ),
             ),
             Prefetch(
                 f"{prefix}c2",
 post_prefetch_callback=create_post_prefetch_callback_add_backward_multiple(
                     retrieve_forward_cache_callback=lambda o:[o.c2] if
 o.c2_id else [],
                     backward_cache_name="current_order_ancestors",
                 ),
             ),
             Prefetch(
                 f"{prefix}s__ps",
                 queryset=(
                     P.objects.filter(c_id=c_id)
                     if c_id
                     else P.objects.all()
                 ),
                 to_attr="needed_ps",
                 filter_callback=lambda p: hasattr(p,
 "_prefetched_objects_cache")
                 and
 p._prefetched_objects_cache.get("current_order_ancestors")
                 and any(
                     map(
                         lambda o: o.c2_id is not None,
                         p._prefetched_objects_cache.get(
                             "current_o_ancestors"
                         ).values(),
                     )
                 ),
                 post_prefetch_callback=ventilate_ps_by_c_id,
             ),
             Prefetch(
                 f"{prefix}c2__u",
                 queryset=C2U.objects.filter(p2_id__isnull=False)
                 .distinct("c2_id")
                 .prefetch_related(
                     "p2",
                     Prefetch(
                         f"p2__ps",
                         queryset=(
                             P.objects.filter(c_id=c_id)
                             if c_id
                             else P.objects.all()
                         ),
                         to_attr="needed_ps",
                         post_prefetch_callback=ventilate_ps_by_c_id,
                     ),
                 ),
                 to_attr="needed_u",
                 filter_callback=lambda c: (
                     hasattr(c2, "_prefetched_objects_cache")
                     and
 c2._prefetched_objects_cache.get("current_o_ancestors")
                     and any(
                         map(
                             lambda o: o.c2_id is not None
                             and o.s_id is None,
                             c._prefetched_objects_cache.get(
                                 "current_o_ancestors"
                             ).values(),
                         )
                     )
                 ),
             ),
         )


 class DModelSerializer(...):
     ...

     @staticmethod
     def enhance_queryset(
         query_set,
         prefix: str = "",
         c_id: Optional[CId] = None,
     ):
         query_set = SomeMixin2.enhance_queryset(
             query_set,
             f"{prefix}l__",
         )
         query_set = OModelSerializer.enhance_queryset(
             query_set,
             f"{prefix}l__",
             c_id=c_id,
         )
         query_set = query_set.prefetch_related(
             f"{prefix}l__s2",
             f"{prefix}l__p3",
             f"{prefix}l2__s3__u",
             Prefetch(
                 f"{prefix}l__r1",
                 queryset=R1.objects.filter(
                     d_id=F("o__s2__d_id"),
                     r2__s4=SOME_CONSTANT,
                 ).select_related("r2"),
                 to_attr="pertinent_r3",
             ),
         )

         return query_set
 }}}
-- 
Ticket URL: <https://code.djangoproject.com/ticket/35317#comment:7>
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/0107018e633aa5c0-d920df18-91dd-414c-9ce5-1ce85c620840-000000%40eu-central-1.amazonses.com.

Reply via email to