Django prefetch_related with limit

Owa*_*one 21 sql django postgresql

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

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

我基本上想转此

   users = User.objects.all()
   for user in users:
       user.comments.all()[:10]
Run Code Online (Sandbox Code Playgroud)

这样的事情

 User.objects.all().prefetch_related('comments', limit=10)
Run Code Online (Sandbox Code Playgroud)

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

has*_*mad 15

我认为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)))
Run Code Online (Sandbox Code Playgroud)

  • @deathangel908我不确定是否可以生成更好的sql以实现主结果列表下的嵌套有限行。我刚刚找到了一种在 Django ORM 中实现嵌套查询限制的方法。如果您想到任何更好的原始 sql,请分享,我们也许能够找到一种为其生成 Django ORM 代码的方法。 (3认同)
  • 你看到它生成的[可怕](https://gist.github.com/akoidan/25a0b87784cb49a1365c6751d43a4305) sql吗?你检查过它的性能吗? (2认同)

C14*_*14L 7

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

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

返回错误

AssertionError: Cannot filter a query once a slice has been taken.
Run Code Online (Sandbox Code Playgroud)

例如,限制相关对象数量的唯一方法似乎是对值使用过滤器

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)))
Run Code Online (Sandbox Code Playgroud)

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

  • 这是一个关于`Prefetch`对象的票证,它不接受带有切片的查询集:https://code.djangoproject.com/ticket/26780 (9认同)
  • [已经在 Django 4.2 中工作](https://docs.djangoproject.com/en/dev/releases/4.2/#models) 但目前处于测试阶段 (3认同)