24

假设我有下表fruits

id | type   | name
-----------------
 0 | apple  | fuji
 1 | apple  | mac
 2 | orange | navel

我的目标是最终计算出不同的types和以逗号分隔的列表names

apple, 2, "fuji,mac"
orange, 1, "navel"

这可以GROUP_CONCAT在 MySQL 中轻松完成,但我在使用 Django 等效项时遇到了麻烦。这是我到目前为止所拥有的,但我错过了GROUP_CONCAT这些东西:

query_set = Fruits.objects.values('type').annotate(count=Count('type')).order_by('-count')

如果可能,我想避免使用原始 SQL 查询。

任何帮助将不胜感激!

谢谢!=)

4

11 回答 11

49

您可以创建自己的聚合函数 ( doc )

from django.db.models import Aggregate

class Concat(Aggregate):
    function = 'GROUP_CONCAT'
    template = '%(function)s(%(distinct)s%(expressions)s)'

    def __init__(self, expression, distinct=False, **extra):
        super(Concat, self).__init__(
            expression,
            distinct='DISTINCT ' if distinct else '',
            output_field=CharField(),
            **extra)

并将其简单地用作:

query_set = Fruits.objects.values('type').annotate(count=Count('type'),
                       name = Concat('name')).order_by('-count')

我正在使用 django 1.8 和 mysql 4.0.3

于 2015-07-10T09:43:39.450 回答
17

注意 Django (>=1.8) 提供Database functions支持。 https://docs.djangoproject.com/en/dev/ref/models/database-functions/#concat

这里是增强版Shashank Singla

from django.db.models import Aggregate, CharField

class GroupConcat(Aggregate):
    function = 'GROUP_CONCAT'
    template = '%(function)s(%(distinct)s%(expressions)s%(ordering)s%(separator)s)'

    def __init__(self, expression, distinct=False, ordering=None, separator=',', **extra):
        super(GroupConcat, self).__init__(
            expression,
            distinct='DISTINCT ' if distinct else '',
            ordering=' ORDER BY %s' % ordering if ordering is not None else '',
            separator=' SEPARATOR "%s"' % separator,
            output_field=CharField(),
            **extra
        )

用法:

LogModel.objects.values('level', 'info').annotate(
    count=Count(1), time=GroupConcat('time', ordering='time DESC', separator=' | ')
).order_by('-time', '-count')
于 2016-11-08T03:57:23.510 回答
8

从我维护GroupConcat的 Django-MySQL 包( https://django-mysql.readthedocs.org/en/latest/aggregates.html#django_mysql.models.GroupConcat )中使用。有了它,您可以像这样简单地做到这一点:

>>> from django_mysql.models import GroupConcat
>>> Fruits.objects.annotate(
...     count=Count('type'),
...     name_list=GroupConcat('name'),
... ).order_by('-count').values('type', 'count', 'name_list')
[{'type': 'apple', 'count': 2, 'name_list': 'fuji,mac'},
 {'type': 'orange', 'count': 1, 'name_list': 'navel'}]
于 2016-03-05T16:22:44.053 回答
4

如果您使用的是 PostgreSQL,则可以使用ArrayAgg将所有值聚合到一个数组中。

https://www.postgresql.org/docs/9.5/static/functions-aggregate.html

于 2018-06-22T16:22:25.137 回答
3

如果您不介意在模板中执行此操作,则 Django 模板标记重新组合可以完成此操作

于 2012-05-04T16:05:50.187 回答
3

从 Django 1.8 开始,您可以使用Func() 表达式

query_set = Fruits.objects.values('type').annotate(
    count=Count('type'),
    name=Func(F('name'), 'GROUP_BY')
).order_by('-count')
于 2015-12-01T17:08:55.260 回答
2

Django ORM 不支持这个;如果您不想使用原始 SQL,那么您需要group 和 join

于 2012-04-26T20:33:59.850 回答
1

Django ORM 不支持,但您可以构建自己的聚合器。

它实际上非常简单,这里是一个链接到一个如何做到这一点,GROUP_CONCAT对于 SQLite:http ://harkablog.com/inside-the-django-orm-aggregates.html

但是请注意,可能需要分别处理不同的 SQL 方言。例如,SQLite 文档说关于group_concat

连接元素的顺序是任意的

MySQL 允许您指定顺序

GROUP_CONCAT我想这可能是它目前没有在 Django 中实现的原因。

于 2014-04-08T23:31:45.777 回答
1

为了完成@WeizhongTu的回答,请注意您不能将关键字SEPARATOR与SQLITE一起使用。如果您使用 MySQL 和 SQLite 进行测试,您可以编写:

class GroupConcat(Aggregate):
    function = 'GROUP_CONCAT'
    separator = ','

    def __init__(self, expression, distinct=False, ordering=None, **extra):
        super(GroupConcat, self).__init__(expression,
                                          distinct='DISTINCT ' if distinct else '',
                                          ordering=' ORDER BY %s' % ordering if ordering is not None else '',
                                          output_field=CharField(),
                                          **extra)

    def as_mysql(self, compiler, connection, separator=separator):
        return super().as_sql(compiler,
                              connection,
                              template='%(function)s(%(distinct)s%(expressions)s%(ordering)s%(separator)s)',
                              separator=' SEPARATOR \'%s\'' % separator)

    def as_sql(self, compiler, connection, **extra):
        return super().as_sql(compiler,
                              connection,
                              template='%(function)s(%(distinct)s%(expressions)s%(ordering)s)',
                              **extra)
于 2019-03-18T07:47:05.483 回答
1

如果您使用任何建议的 MySQL 解决方案,我只想说一句警告:默认情况下,MySQL 会将 GROUP_CONCAT 结果截断为 1024 个字符。我花了一些时间弄清楚为什么我得到了不存在的 id(它们被截断了存在的 id)。

您可以通过在 Django 设置中设置 group_concat_max_len 来避免限制。一个例子在这里:在 Django 的原始查询中包含多个语句

于 2021-08-24T20:22:32.450 回答
0

这是在 Django ORM 中工作的最佳方式

f1 = Fruits.objects.values('type').annotate(count = Count('type'),namelist= GroupConcat('namelist')).distinct()
于 2022-01-10T13:58:45.113 回答