Django prefetch_related with limit

时间:2014-07-11 05:41:31

标签: sql django postgresql

有没有办法告诉prefetch_related只获取一组有限的相关对象?让我们说我正在获取一个用户列表,我知道我想获取他们最近的评论。我没有在循环中为每个用户提取注释,而是在获取用户时使用prefetch_related预取它们。我的理解是,这将获取原始查询结果中出现的任何用户所做的所有评论,但我只想为每个用户显示最新的5条。

如果评论列表真的很大,这对性能有何影响?有没有办法在单个(或2个)查询中为每个用户仅获取5条评论?它不必与用于获取用户的原始查询相同,但这样会很好。

我基本上想转此

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

这样的事情

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

因此,如果用户有100或10000条评论,则它们不会全部加载到内存中。你会如何在原始SQL中做这样的事情?

3 个答案:

答案 0 :(得分:9)

我认为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)))

答案 1 :(得分:5)

限制预取相关对象数量的唯一方法似乎是使用Prefetch()和对fileds进行过滤。使用切片

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

虽然这不会返回固定数字,但在某些情况下可能会有用,并且会减少预取对象的数量。

答案 2 :(得分:2)

多数民众赞成在我真正的工作django(2.1)(基于haseebahmad答案)。
为了使prefetch_related接受自定义查询集:Prefetch
所以:
    从django.db.models导入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]))))
相关问题