【问题标题】:How to avoid n+1 select in django?如何避免在 django 中选择 n+1?
【发布时间】:2011-06-17 16:16:20
【问题描述】:

我有一个非常简单的数据模型,视频和 cmets 之间存在一对多关系

class Video(models.Model):
    url = models.URLField(unique=True)
    .....

class Comment(models.Model):
    title = models.CharField(max_length=128)
    video = models.ForeignKey('Video')
        .....

我想查询视频并获取整个对象图(包含所有 cmets 的视频)。查看 sql,我看到它做了两个选择,一个用于视频,一个用于评论。我该如何避免呢?我想加入并立即获取所有内容。

django 可以做到这一点吗?

【问题讨论】:

    标签: django orm join


    【解决方案1】:

    For ForeignKey,可以使用selected_related():

    Comment.objects.select_related('video').all()
    

    它只会生成一个查询,为您收集评论以及视频。

    对于更复杂的事情(例如 M2M),您需要一个外部应用程序(例如 unjoinify)进行优化,但它使用 SQL 查询然后将它们放回对象中。

    如果您对此不满意(我是),您有一些选择:

    【讨论】:

    • 我想做相反的事情,从视频中获取 cmets。我想查询视频但获取这些视频的所有 cmets 并生成如下 sql 语句: select * from video v join v.id=c.video_id where v.date>some_date.我们的想法是在一个查询中获取所有视频及其 cmets。
    • 抱歉,我要生成的 sql 语句不正确。我想生成以下内容:select * from Videos v left join Comment c on v.id = c.id where v.date>some_date。基本上我想要所有的视频和他们的 cmets,甚至是没有 cmets 的视频。此示例 Comment.objects.filter(video__title__starts_with='The') .select_related('video').all() 不会获取没有 cmets 的视频。
    • 这就是为什么我给你一个链接到其他工具的链接,比如 django-batch-select 可以为你做到这一点:“大概是一个 select_related 与 M2M 和 REVERSE RELATIONS 一起工作”。
    • M2M 关系有什么更新吗?这里指出的库已经 10 多年没有更新了...
    【解决方案2】:

    您需要做的是在评论上使用 select_related。

    假设您需要找到标题以“The”开头的所有视频以及与之相关的 cmets

    comments = Comment.objects.filter(video__title__starts_with='The')
               .select_related('video').all()
    

    这将加载该评论的所有 cmets 和相应的 Video 对象。您仍然需要在视频上进行旋转以迭代视频。使用 python itertools.groupby 函数在内存中进行旋转。

    【讨论】:

      【解决方案3】:

      看看select related 是否按您期望的方式工作,它就是为此而生的。

      【讨论】:

        猜你喜欢
        • 2012-05-19
        • 2015-11-28
        • 1970-01-01
        • 2011-12-07
        • 2011-07-12
        • 1970-01-01
        • 2018-11-30
        • 2012-09-29
        • 1970-01-01
        相关资源
        最近更新 更多