Sql 与限制相关的Django预取

Sql 与限制相关的Django预取,sql,django,postgresql,Sql,Django,Postgresql,有没有办法告诉预取\u related只获取有限的相关对象集?假设我正在获取一个用户列表,我知道我想获取他们最近的评论。与在循环中获取每个用户的注释不同,我使用了与在获取用户时预获取注释相关的prefetch_。我的理解是,这将获取原始查询结果中任何用户的所有评论,但我只想为每个用户显示最新的5条评论 如果评论列表非常庞大,这会如何影响性能?是否有一种方法可以在单个(或2个)查询中为每个用户仅获取5条注释?对于获取用户,它不必是与原始查询相同的查询,但这会很好 我基本上想扭转这种局面 us

有没有办法告诉
预取\u related
只获取有限的相关对象集?假设我正在获取一个用户列表,我知道我想获取他们最近的评论。与在循环中获取每个用户的注释不同,我使用了与在获取用户时预获取注释相关的prefetch_。我的理解是,这将获取原始查询结果中任何用户的所有评论,但我只想为每个用户显示最新的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中执行类似操作?

限制预取相关对象数量的唯一方法似乎是使用Prefetch()并对文件进行筛选。使用切片

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

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

我认为现在有一个解决办法可以在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)))

这就是django(2.1)(基于答案)对我实际起作用的原因。
与接受自定义查询相关的预回迁顺序设置:预回迁
因此:


我不认为使用预取是一个好方法。事实上,prefetch_related对每个关系进行单独的查找,并在Python中进行连接。这意味着您将在python中预加载注释,并且将从预加载的注释列表中完成连接。在你的情况下,确保每个用户都有最后10条评论,你需要预加载所有评论。我可以接受每个关系一个查询,但每个对象一个查询才是真正的杀手。我同意,每个对象一个查询是一场噩梦。但是为什么不这样做呢:
users=User.objects.all().prefetch_related('comments')
在这种情况下,您将只执行2个查询如果comments表中有成千上万个与所选用户关联的行呢?用户评论关系不太可能出现这种情况,但在其他情况下很可能出现这种情况。我担心的是,每选择10-20个用户,就会获取数百或数千条评论,并在python中加入它们(想想分页)可能会有性能问题。在这种情况下,我最好的办法可能是在redis或denormalize中缓存前10条注释。下面是一张关于
预回迁
对象不接受带切片的查询集的罚单:您看到它生成的sql了吗?你用它检查了性能吗?@deatangel908我不确定是否可以生成更好的sql,以实现主结果列表下嵌套的有限行。我刚刚找到了一种在Django ORM中实现嵌套查询限制的方法。如果你想到了更好的原始sql,请与我们分享,我们也许能够找到一种方法来生成Django ORM代码。请看@haseebahmad,如果我需要限制传入预回迁相关对象函数的预回迁,我该如何使用这种方法?@Desh我想你可以传递相同的“预回迁”('comments',queryset=Comment.objects.filter(id_uin=subqry))”以预取与_相关的_对象作为第二个参数。尚未对其进行测试,但我认为它可以工作
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]))))