【问题标题】:Getting N related models given M models给定 M 个模型得到 N 个相关模型
【发布时间】:2013-03-15 11:06:49
【问题描述】:

给定两个模型:

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

【问题讨论】:

  • This 并没有给我太多希望,通过 ORM 很容易做到这一点。

标签: sql django django-orm


【解决方案1】:

来自Django's docs

切片。正如限制 QuerySets 中所解释的,可以使用 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 cmets,我确信这是可能的,但我想不出我脑海中的语法。另外,请记住,您始终可以对任何模型进行手动查询以获得更好的优化,因此您可以运行 Comment.query("Select ...;")

鉴于here 关于“从组中选择前 N 个”问题的信息,如果您是 IN 子句将是少量帖子,那么 a) 执行多个查询或b) 为帖子选择所有 cmets,然后在 Python 中过滤。我建议使用 a 如果它是包含大量 cmets 的少量帖子,如果每个帖子的 cmets 相对较少,我建议使用 b。

【讨论】:

  • 我已经为幼稚的实现和我正在寻找的解决方案添加了更多解释。从本质上讲,我希望对整个 cmets 集进行单个数据库查询,而不是每个帖子一个。
  • +1 为有趣的链接,看起来这绝对不是 ORM 可以轻松解决的问题。
  • @bismark 也很高兴,只要确保您在更新中收到了 post__in=some_post_ids。您不需要实际的模型实例,外键字段将与模型或 id 匹配(即该 db 列中的实际值)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-07-27
  • 1970-01-01
  • 2019-11-03
  • 2020-09-30
  • 2020-05-25
相关资源
最近更新 更多