Django prefetch_related с лимитом

Есть ли способ сказать prefetch_related только для получения ограниченного набора связанных объектов? Допустим, я собираю список пользователей, и я знаю, что хочу получить их последние комментарии. Вместо того, чтобы получать комментарии для каждого пользователя в цикле, я использую prefetch_related для предварительного извлечения их во время выборки пользователей. Я понимаю, что это позволит получить все комментарии, сделанные любым пользователем, присутствующим в результате исходного запроса, но я хочу показать только последние 5 для каждого пользователя.

Как это влияет на производительность, если список комментариев действительно огромен? Есть ли способ получить только 5 комментариев для каждого пользователя в одном (или 2) запросе? Это не обязательно должен быть тот же самый запрос, что и исходный для извлечения пользователей, но это было бы неплохо.

Я хочу повернуть этот

   users = User.objects.all()
   for user in users:
       user.comments.all()[:10]

во что-то подобное

 User.objects.all().prefetch_related('comments', limit=10)

поэтому, если пользователь имеет 100 или 10000 комментариев, они не все загружаются в память. Как бы вы сделали что-то подобное в сыром SQL?

Ответы

Ответ 1

Я думаю, что теперь есть обходной путь в новой версии django, так как у нас есть OuterRef и 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)))

Ответ 2

Единственный способ ограничить количество предварительно заданных связанных объектов, по-видимому, использует Prefetch() и фильтрацию на файлах. Использование sliceing

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

возвращает ошибку

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

Единственный способ ограничить количество связанных объектов, по-видимому, использует фильтр для значения, например

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)))

Пока это не возвращает фиксированное число, в некоторых случаях может быть полезно, и это уменьшит количество предварительно загруженных объектов.

Ответ 3

вот что на самом деле работает для меня django (2.1) (на основе ответа haseebahmad).
для того, чтобы prefetch_related принимал настроить набор запросов: Prefetch
так:
из 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]))))