【问题标题】:Django ORM: get objects distinct on a column and ordered by another columnDjango ORM:获取列上不同的对象并按另一列排序
【发布时间】:2014-07-16 18:28:44
【问题描述】:

我有一个 Django 模型(极其简化),如下所示:

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

我想获取唯一的numbers 列表,按它们出现的时间倒序排列。

以这些数据为例(dates 在此处排序以提高可读性):

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

我会得到一个如下所示的列表:

[12, 8, 5, 14]

直觉上我会这样写查询:

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

但这是无效的 ORM 代码。

我该如何解决这个问题?

请注意,有问题的表包含数百万行可能有很多重复的numbers,所以我无法以“幼稚”的方式解决这个问题(例如,遍历来自MyModel.objects.order_by('-date') 的行并选择numbers 如果他们之前没有被人看到过,请立即联系他们)。

【问题讨论】:

  • 试试这套(MyModel.objects.order_by('-date').values('number'))
  • @Sławek 这行不通;在 Python 中,set 是无序的。因此,不能保证number 值的顺序正确。此外,这会导致对整个查询集进行评估,如果您有数百万行,这将是一个问题。

标签: python django django-orm


【解决方案1】:

您可以使用 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]

【讨论】:

    【解决方案2】:

    @mipadi's answer 效果很好,但我更愿意在我的代码库中避免使用原始 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: Cannot resolve keyword 'm' into field. 错误。可能有办法解决这个问题,但我并没有被 2 行版本困扰到花时间弄清楚它。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-04-08
      • 2014-06-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-10-23
      • 2010-11-04
      • 1970-01-01
      相关资源
      最近更新 更多