Changes between Version 4 and Version 6 of Ticket #34413


Ignore:
Timestamp:
Mar 13, 2023, 5:00:20 PM (21 months ago)
Author:
Willem Van Onsem
Comment:

Legend:

Unmodified
Added
Removed
Modified
  • Ticket #34413 – Description

    v4 v6  
    1 What would happen if we span over multiple relations, like:
     1A frequently asked feature that seems to be missing is fetch the earlest/latest related object for each item. Indeed, we can for example work with a subquery to fetch *the primary key* of the earliest/latest related object, but not that object itself.
     2
     3It turns out however that automating this is not that complicated. What we need to do is (automatically) construct a reverse filter, so if we want the latest `Comment` of each `Post`, we make a queryset that makes a `Comment.objects.filter(post_id=OuterRef('pk'))`, then we convert that into a subquery that will, for each `Post` fetch the primary key of the latest comment, and slightly alter the logic that is already used to for prefetching to then prefetch all the `Comment`s for these primary keys, and add attributes to the corresponding `Post`s.               
     4
     5I made a small proof-of-concept that should normally work for (most) cases: it allows one to use an arbitrary queryset and specify an ordering (or use the ordering already in the queryset or by the model). It can also span over multiple layers. It requires to *always* specify a `to_attr`, since, especially when spanning over multiple relations, the default `to_attr` would require double underscores.             
     6
     7The most ugly part is that we work with an annotation that is then accessible by the user, so it is not somehow hidden. Technically we could remove the attribute, or do something extra in the ORM to prevent exposing this attribute.                 
     8
     9I did not yet check what querysets will be problematic. For example a sliced queryset would (very likely) not work, and likely most/all restrictions already in place for a the queryset of a `Prefetch` object are still applicable.
     10
     11A simple demonstration of the `PrefetchEarliest` could be:             
    212
    313{{{
    4 User.objects.prefetch_related(
    5     Prefetch("groups__permissions", queryset=Permission.objects.order_by('codename')[:1], to_attr="first_permission")
    6 )
     14User.objects.prefetch_related(PrefetchEarliest('groups', to_attr='first_group', Group.objects.order_by('name'))                 
    715}}}
Back to Top