1

给定两个模型:

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'}]
4

1 回答 1

1

来自Django 的文档

切片。如限制查询集中所述,可以使用 Python 的数组切片语法对查询集进行切片。对未计算的 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 个”问题的信息,如果您的IN子句将是少量帖子,那么 a) 执行多个查询或 b) 选择帖子的所有评论可能会更便宜然后在 Python 中过滤。我建议使用 a 如果它是有很多评论的少量帖子,如果每个帖子的评论相对较少,我建议使用 b。

于 2013-03-25T20:10:47.100 回答