鉴于两种模式:
class Post(models.Model):
...
class Comment(models.Model):
post = models.ForeignKey(Post)
...
有没有一种方法可以在一组帖子上获得最后3条评论(即在一次往返数据包而不是每次发布一次)?一个天真的实现,以显示我的意思:
for post in Post.objects.filter(id__in=some_post_ids):
post.latest_comments = list(Comment.objects.filter(post=post).order_by('-id')[:3])
鉴于some_post_ids == [1, 2]
,上述内容将导致3次查询:
[{'sql': 'SELECT "myapp_post"."id" FROM "myapp_post" WHERE "myapp_post"."id" IN (1, 2)', 'time': '0.001'},
{'sql': 'SELECT "myapp_comment"."id", "myapp_comment"."post_id" FROM "myapp_comment" WHERE "myapp_comment"."post_id" = 1 LIMIT 3', 'time': '0.001'},
{'sql': 'SELECT "myapp_comment"."id", "myapp_comment"."post_id" FROM "myapp_comment" WHERE "myapp_comment"."post_id" = 2 LIMIT 3', 'time': '0.001'}]
答案 0 :(得分:1)
切片。如限制查询集中所述,可以使用Python的数组切片语法对QuerySet进行切片。切片未评估的QuerySet通常会返回另一个未评估的QuerySet,但如果使用切片语法的“step”参数,Django将执行数据库查询,并返回一个列表。切片已经评估过的QuerySet(部分或全部)也会返回一个列表。
您的天真实现是正确的,只应进行一次数据库查询。但是,不要在其上调用list
,我相信这会导致数据库立即被命中(尽管它应该仍然只是一个查询)。它已经可迭代的查询集,实际上不应该需要调用list
。有关从同一个文档页面调用list
的更多信息:
列表()。通过调用list()来强制评估QuerySet。例如:
entry_list = list(Entry.objects.all())
但是要注意,这可能会产生很大的内存开销,因为Django会将列表中的每个元素加载到内存中。相反,迭代QuerySet将利用您的数据库加载数据并仅在您需要时实例化对象。
<强>更新强>:
根据您添加的解释,我相信以下内容应该有效(但是,未经测试,请回报!):
post.latest_comments = Comment.objects.filter(post__in=some_post_ids).order_by('-id')
不可否认,它不会对每篇帖子发表3条评论的限制,我确信这是可能的,但我无法想到我的头脑中的语法。此外,请记住,您始终可以对任何模型进行手动查询以获得更好的优化,因此您可以运行Comment.query("Select ...;")
鉴于“从组中选择前N个”问题的here信息,如果你的IN
条款将是少量的帖子,它可能只是更便宜a)做多个查询或b)选择帖子的所有评论然后在Python中过滤。如果每个帖子的评论数量相对较少,我建议使用if,如果它是少量帖子,那么每个帖子的评论相对较少。