Django ORM: 根据某一列进行去重并按另一列排序获取对象

3

我有一个 Django 模型,它(极度简化)看起来像这样:

class MyModel(models.Model):
    date = models.DateTimeField()
    number = models.PositiveIntegerField()

我希望能够获取一份独特的数字列表,按它们出现的时间顺序(倒序)排列。

例如,对于以下数据(日期按照更好的可读性进行排序):

date         | number
-------------+-------
24/12/2014   | 12
23/12/2014   | 8
22/12/2014   | 8
21/12/2014   | 5
20/12/2014   | 12
19/12/2014   | 5
18/12/2014   | 14

我将获得以下类似列表:
```HTML

我将得到以下列表:

```
[12, 8, 5, 14]

直观地,我会像这样编写查询:

MyModel.objects.order_by('-date').distinct('number')

但这是无效的ORM代码。

我该如何解决这个问题?

请注意,所涉及的表可能包含数百万行,其中可能有很多重复的number,因此我不能用“幼稚”的方式解决这个问题(例如,从MyModel.objects.order_by('-date')迭代行,并在运行时选择未被看到的number)。


尝试使用这个集合(MyModel.objects.order_by('-date').values('number'))。 - Sławek Kabik
@Sławek 这样做是行不通的;在Python中,set是无序的。因此,不能保证number值的顺序正确。此外,这将导致整个查询集被评估,如果你有数百万行数据,这将会带来问题。 - bitgarden
2个回答

2

@mipadi的答案很好,但我更愿意在我的代码库中避免使用原生SQL。

然而,他的答案使得想出本地ORM版本更容易了,所以感谢他!这是我想出的ORM版本:

 results = MyModel.objects.values('number').annotate(m=Max('date')).order_by('-m')
 results = [r.number for r in results]

看起来应该可以做到以下操作:

results = MyModel.objects.values('number').annotate(m=Max('date')).order_by('-m').values_list('number', flat=True)

但是在Django 1.6中,当添加最后一个values_list方法时,我会遇到一个FieldError:无法将关键字“m”解析为字段。错误。可能有一种方法可以解决这个问题,但我不想花时间去弄清楚它,因为这两行代码的版本已经足够好了。

0

您可以使用 SQL 的 GROUP BY 语句来实现此操作,但说实话,我经常忘记如何在 Django 的 ORM 中使用 GROUP BY,所以我会使用原始的 SQL:

number_qs = MyModel.objects.raw("""
    SELECT
        id, MAX(date) AS date, number
    FROM
        myapp_mymodel
    GROUP BY
        number
    ORDER BY
        MAX(date) DESC;
""")
numbers = [n.number for n in number_qs]

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接