Django,按一个字段分组,只取每组的最新/最大值并返回 ORM 对象

pis*_*hio 4 python django orm django-models

我有以下 Django (3.0) ORM 模型:

class Portfolio(models.Model):
    code = models.CharField(max_length=20)
    name = models.CharField(max_length=100)
    date = models.DateField()
    created = models.DateTimeField(blank=True, null=True, auto_now_add=True)
Run Code Online (Sandbox Code Playgroud)

我想按字段对它们进行分组code,并且对于每个代码,仅采用具有最大值的投资组合date

我知道我可以使用以下查询:

Portfolio.objects.values('code').annotate(latest=Max('date'))
Run Code Online (Sandbox Code Playgroud)

但它存在三个问题:

  1. 它只给了我code和latest字段,所以我失去了其他字段
  2. 它返回一本字典,而我想要一个实际Portfolio对象的列表
  3. Max有效,因为date是 a DateField. 它也适用于其他数字字段类型,但是如果我想按 CharField 的值(字典顺序)对记录进行排序,name并获取每个组的第一条记录,该怎么办?

所以,总而言之,我的问题是:如何使用 Django ORM 检索按一个或多个字段分组的 ORM 对象列表,并仅返回给定任意“order by”子句的每个组的第一个记录?

Ole*_*kin 8

从组中获取第一个/最后一个值正是DISTINCT ON 与 ORDER BY SQL 子句的用途(但仅在 Postgresql 中。在 MySQL 中DISTINCT,不ON,所以 -不可能(直接),SQLite 也不支持DISTINCT ON,只是DISTINCT。为了表明这一点,在 Django 中,位置参数.distinct()只能在 Postgresql 中传递)。

在 Django 中,我们可以使用QuerySet来做到这一点,如下所示:

Portfolio.objects.order_by().order_by(
    'code', # first, cause we want to group by this value
    '-created' # descending order, latest / max will be first
).distinct('code')
Run Code Online (Sandbox Code Playgroud)

在这里,我们使用空调.order_by()用来__clear QuerySet上已存在的所有排序(添加或默认),以确保仅将所需的排序应用于 next.order_by(...)以便分组正常工作。


一般使用方法:

  • 启动QuerySet并应用过滤器 -q = SomeModel.objects.filter(col1__gt=2)

  • 已在QuerySet上设置的明确排序-q.order_by()

  • 首先添加分组列(distinct稍后使用)的排序,然后添加附加列 -q.order_by('col1', '-col2', '-col3', '-date1')

    这里:

    • 'col1'、'col2'、'col3' - 是我们想要的列/字段GROUP BY(用于分组)

    • 'col1'、'-col2'、'-col3' - 我们想要分组的相同列,但具有我们想要用于分组的顺序(对于所有子组至关重要 - 组列表中除第一个列之外的所有列-对于他们来说,它将影响从组中获取的结果行 - 'first'或'last';对于第一列,它不会影响结果行,只会影响结果排序)

    • '-date1' - 我们选择的任何其他排序列来对最终组行进行排序
  • 最后添加.distinct()子句,其中包含我们选择作为参数分组的字段,顺序与.order_by()子句中的顺序相同 -q.distinct('col1', 'col2', 'col3')

  • 尝试将“DISTINCT ON”与 SQLite 后端一起使用将触发后端引发“NotSupportedError”。 (3认同)