【发布时间】:2014-12-07 00:28:00
【问题描述】:
我有一个非常标准、基本的社交应用程序——带有状态更新(即帖子),每个帖子有多个 cmets。
鉴于以下简化模型,是否有可能使用 Django 的 ORM 高效地检索所有帖子以及与每个帖子关联的最新两个 cmets,而无需执行 N+1 查询? (也就是说,无需执行单独的查询来获取页面上每个帖子的最新 cmets。)
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']
Post.objects.prefetch_related('comments').all() 获取所有帖子和 cmets,但我只想检索每个帖子的有限数量的 cmets。
更新:
我知道,如果这完全可以使用 Django 的 ORM 来完成,那么它可能必须使用 prefetch_related 的某个版本来完成。多个查询完全没问题,只要我避免每页进行 N+1 个查询。
在 Django 中处理此问题的典型/推荐方法是什么?
更新 2:
似乎没有直接简单的方法可以通过使用 Django ORM 的简单查询来有效地执行此操作。以下答案中有许多有用的解决方案/方法/解决方法,包括:
- 在数据库中缓存最新的评论 ID
- 执行原始 SQL 查询
- 检索所有评论 ID 并在 python 中进行分组和“加入”
- 将您的应用程序限制为仅显示最新评论
我不知道应该将哪一个标记为正确,因为我还没有机会尝试所有这些方法 - 但我将赏金奖励给了 hynekcer,因为它提供了许多选项。
更新 3:
我最终使用了@user1583799 的解决方案。
【问题讨论】:
-
我不确定
.select_related('comments')是否获取 cmets。.select_related可以获取ForeignKey、OneToOne关系和reverse-OneToOne -
@Igor,嗯,我没有意识到是这样的。我猜prefetch_related 上的文档暗示了这一点。感谢您的提醒。
-
获取所有相关的 cmets 有什么问题?您以后只能为每个帖子使用前两个。
posts[0].comments.all()不会执行额外的查询。问题是有太多相关查询无法全部预取吗? -
@KrzysztofSzularz 感谢您的回复。每页有 20 个帖子,每个帖子有数百个 cmets。似乎我要么执行 31 次查询以获取将显示的 60 个 cmets。或者 2 个查询来预取并加载到内存中数千个 cmets,其中 99% 不会显示。
标签: django django-models django-queryset