[Django]-Django prefetch_related with limit

41👍

I think there is a workaround now to in django new version as we have OuterRef and Subquery.

from django.db.models import OuterRef, Subquery, Prefetch

subqry = Subquery(Comment.objects \
    .filter(user_id=OuterRef('user_id')) \
    .values_list('id', flat=True)[:5])

User.objects.prefetch_related(
    Prefetch('comments', queryset=Comment.objects.filter(id__in=subqry)))

12👍

The only way to limit the number of prefetched related objects seems to be using Prefetch() and filtering on fileds. Using sliceing

User.objects.all().prefetch_related(
    Prefetch('msg_sent', queryset=UserMsg.objects.order_by('-created')[:10]))

returns an error

AssertionError: Cannot filter a query once a slice has been taken.

The only way to limit the number of related objects seems to be using filter on a value, for example

from datetime import datetime, timedelta
timelimit = datetime.now() - timedelta(days=365)

User.objects.all().prefetch_related(
    Prefetch('msg_sent', queryset=UserMsg.objects.filter(created__gte=timelimit)))

While that doesn’t return a fixed number, in may be useful in some situation, and it will reduce the number of prefetched objects.

👤C14L

4👍

thats what actually works for me django(2.1) (based on haseebahmad answer).
in order for prefetch_related to accept customize queryset: Prefetch
so:

from django.db.models import OuterRef, Subquery ,Prefetch

User.objects.all().prefetch_related(Prefetch('comment_set',  
queryset=Comment.objects.filter(id__in= 
Subquery(Comment.objects.filter(user_id=OuterRef('user_id')).
values_list('id', flat=True)[:1]))))
👤Rafi

0👍

It is also possible to do by using CTE and ROW_NUMBER().

from django.db.models import Prefetch
from django.db.models.functions.window import RowNumber
from django_cte import With

cte = With(
    Comment.objects.annotate(
        row_number=Window(
            expression=RowNumber(),
            partition_by=F("user_id")
        )
    )
)
qs = cte.with_cte(cte).filter(row_number<=10)
users = User.objects.prefetch_related(
    Prefetch("comments", queryset=qs, to_attr="limited_comments")
)

Leave a comment