Django ORM:在不执行N + 1查询的情况下检索帖子和最新评论

tin*_*ino 5 django django-models django-queryset

我有一个非常标准的基本社交应用程序-状态更新(即帖子),每个帖子有多个评论。

给定以下简化的模型,是否有可能使用Django的ORM,高效地检索所有帖子以及与每个帖子相关的最新两个评论,而无需执行N + 1查询?(也就是说,无需执行单独的查询即可获取页面上每个帖子的最新评论。)

class Post(models.Model):
    title = models.CharField(max_length=255)
    text = models.TextField()

class Comment(models.Model):
    text = models.TextField()
    post = models.ForeignKey(Post, related_name='comments')

    class Meta:
        ordering = ['-pk']
Run Code Online (Sandbox Code Playgroud)

Post.objects.prefetch_related('comments').all() 提取所有帖子和评论,但我只想在每个帖子中检索有限数量的评论。

更新:

我了解,如果可以使用Django的ORM完全完成此操作,则可能必须使用的某些版本来完成prefetch_related。只要我避免每页进行N + 1个查询,完全可以进行多个查询。

在Django中处理此问题的典型/推荐方式是什么?

更新2:

使用Django ORM进行简单查询似乎没有直接,简单的方法来有效地完成此操作。以下答案提供了许多有用的解决方案/方法/解决方法,包括:

  • 在数据库中缓存最新的评论ID
  • 执行原始SQL查询
  • 检索所有注释ID,并在python中进行分组和“加入”
  • 将您的应用程序限制为仅显示最新评论

我不知道哪个标记为正确的,因为我还没有机会尝试所有这些方法-但是我将赏金授予Hynekcer,因为他提出了许多选择。

更新3:

我最终使用了@ user1583799的解决方案。

use*_*799 1

prefetch_related('comments')将获取帖子的所有评论。

我遇到了同样的问题,数据库是Postgresql。我找到了一个方法:

添加额外的字段related_replies。注意 FieldType 为ArrayField,django1.8dev 支持。我将代码复制到我的项目中(django的版本是1.7),只需更改2行,它就可以工作。(或使用djorm-pg-array

class Post(models.Model): related_replies = ArrayField(models.IntegerField(), size=10, null=True)

并使用两个查询:

posts = model.Post.object.filter()

related_replies_id = chain(*[p.related_replies for p in posts])
related_replies = models.Comment.objects.filter(
    id__in=related_replies_id).select_related('created_by')[::1]  # cache queryset

for p in posts:
    p.get_related_replies = [r for r in related_replies if r.post_id == p.id]
Run Code Online (Sandbox Code Playgroud)

当有新评论出现时,更新related_replies