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