【发布时间】: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
我通过以下方式设置了 Django 模型:
模型 A 与模型 B 具有一对多关系
A 中的每条记录在 B 中都有 3,000 到 15,000 条记录
构建一个查询的最佳方法是检索 B 中的最新(最大 pk)记录,该记录对应于 A 中的每条记录的记录?这是我必须使用 SQL 来代替 Django ORM 的东西吗?
【问题讨论】:
标签: sql database django-models
创建一个辅助函数,用于从任何查询集中安全地提取“顶部”项目。我在自己的 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'))
【讨论】:
我不认为 Django ORM 可以做到这一点(但我之前一直很惊喜......)。如果有合理数量的 A 记录(或者如果您正在分页),我只需向 A 模型添加一个方法,该方法将返回此“最新”B 记录。如果你想获得很多 A 记录,每条记录都有自己最新的 B,我会使用 SQL。
记住,无论你走哪条路,你都需要一个合适的复合 B表索引,也许在Meta子类中添加一个order_by=('a_fk','-id')
【讨论】: