按最大列值过滤Django查询记录

11 投票
3 回答
6123 浏览
提问于 2025-04-16 23:21

有没有简单的方法可以根据某一列的最大值或最小值来过滤Django查询?我其实是在问这些 问题,不过是针对Django的ORM。

比如说,

我有一个模型是用来存储每个人的电话号码历史记录的。

class Person(models.Model):
    name = models.CharField(max_length=100)
    phone = models.CharField(max_length=100)
    created = models.DateTimeField(auto_now_add=True)

记录如下:

Person(name='Jim',phone='123-456-9870', created=datetime(2005,1,2,4,2))
Person(name='Jim',phone='329-802-9870', created=datetime(2006,9,2,7,8))
Person(name='Sue',phone='324-345-3450', created=datetime(2008,7,4,6,1))

现在假设我想找出每个人最近的电话号码。

在SQL中,我通常需要使用子查询来计算最大值:

SELECT p1.name, p1.phone, p1.created
FROM person_person p1, (
    SELECT name, MAX(created) AS max_created
    FROM person_person
    GROUP BY name
) AS p2
WHERE p1.name = p2.name AND p1.created = p2.max_created

在Django中有没有什么机制可以简化这个过程?

我在后端使用的是PostgreSQL,所以任何依赖于PostgreSQL特定功能的想法或解决方案都很有帮助。

3 个回答

1

如果你的后台使用的是PostgreSQL,Roman Pekar 给了一个很好的答案,关于这个问题。

3

更新:如果你在使用PostgreSQL,可以用ORM加上.distinct()来实现

来自 PostgreSQL文档

SELECT DISTINCT ON ( 表达式 [, ...] ) 只保留每组行中,给定表达式计算结果相同的第一行。DISTINCT ON 的表达式和 ORDER BY 的规则是一样的(见上文)。需要注意的是,除非使用 ORDER BY 来确保你想要的那一行排在最前面,否则每组的“第一行”是不可预测的。

使用Django的ORM:


Person.objects.order_by('name', '-created').distinct('name')

生成的SQL:

select distinct on (name)
    ...
from person_person
order by name, created desc
5

你可能只想在这里使用原始的SQL语句,Django提供的raw()方法可以帮助你做到这一点,它允许你从查询中返回模型实例。唯一需要注意的是,原始查询必须包含主键。只要你的主键不是设置为其他值(比如不是id),这个方法应该能正常工作:

latest_phone_numbers = Person.objects.raw('''
SELECT p1.id, p1.name, p1.phone, p1.created
FROM person_person p1, (
    SELECT name, MAX(created) AS max_created
    FROM person_person
    GROUP BY name
) AS p2
WHERE p1.name = p2.name AND p1.created = p2.max_created
''')

撰写回答