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