【问题标题】:Django query for large number of relationshipsDjango查询大量关系
【发布时间】:2010-11-20 18:38:32
【问题描述】:

我通过以下方式设置了 Django 模型:

模型 A 与模型 B 具有一对多关系

A 中的每条记录在 B 中都有 3,000 到 15,000 条记录

构建一个查询的最佳方法是检索 B 中的最新(最大 pk)记录,该记录对应于 A 中的每条记录的记录?这是我必须使用 SQL 来代替 Django ORM 的东西吗?

【问题讨论】:

    标签: sql database django-models


    【解决方案1】:

    创建一个辅助函数,用于从任何查询集中安全地提取“顶部”项目。我在自己的 Django 应用程序中到处使用它。

    def top_or_none(queryset):
        """Safely pulls off the top element in a queryset"""
        # Extracts a single element collection w/ top item
        result = queryset[0:1]
    
        # Return that element or None if there weren't any matches
        return result[0] if result else None
    

    这使用了slice operator to add a limit clause onto your SQL 的一些技巧。

    现在在任何需要获取查询集的“顶部”项的地方使用此函数。在这种情况下,您希望获取给定 A 的顶部 B 项目,其中 B 按 pk 降序排序,如下所示:

    latest = top_or_none(B.objects.filter(a=my_a).order_by('-pk'))
    

    Django Aggregation 中还有最近添加的“Max”功能,它可以帮助您获得最大 pk,但我不喜欢这种情况下的解决方案,因为它增加了复杂性。

    附:我不太喜欢依赖 'pk' 字段进行此类查询,因为某些 RDBMS 不保证顺序 pk 与逻辑创建顺序相同。如果我知道我需要以这种方式查询的表,我通常有自己的“创建”日期时间列,我可以使用它来排序而不是 pk。

    根据评论编辑:

    如果你更喜欢使用 queryset[0],你可以这样修改 'top_or_none' 函数:

    def top_or_none(queryset):
        """Safely pulls off the top element in a queryset"""
        try:
            return queryset[0]
        except IndexError:
            return None
    

    我最初没有提出这个建议,因为我的印象是 queryset[0] 会拉回整个结果集,然后取第 0 项。显然 Django 在这种情况下也添加了“LIMIT 1”,因此它是我的切片版本的安全替代方案。

    编辑 2

    当然,您也可以在此处利用 Django 的相关管理器构造,并通过您的“A”对象构建查询集,具体取决于您的偏好:

    latest = top_or_none(my_a.b_set.order_by('-pk'))
    

    【讨论】:

    • result = queryset[0:1] 和 result = queryset[0] 有什么区别?
    • queryset[0:1] 将在没有匹配项时返回一个空列表,而 queryset[0] 将抛出一个 IndexError。
    【解决方案2】:

    我不认为 Django ORM 可以做到这一点(但我之前一直很惊喜......)。如果有合理数量的 A 记录(或者如果您正在分页),我只需向 A 模型添加一个方法,该方法将返回此“最新”B 记录。如果你想获得很多 A 记录,每条记录都有自己最新的 B,我会使用 SQL。

    记住,无论你走哪条路,你都需要一个合适的复合 B表索引,也许在Meta子类中添加一个order_by=('a_fk','-id')

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-09-06
      • 2011-09-20
      • 2013-08-26
      • 1970-01-01
      • 2020-01-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多