Django中的GROUP_CONCAT等价物

时间:2012-04-26 20:07:18

标签: mysql django group-concat

假设我有下表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中的GROUP_CONCAT轻松完成,但我遇到了与Django等效的问题。这是我到目前为止,但我错过了query_set = Fruits.objects.values('type').annotate(count=Count('type')).order_by('-count') 的东西:

{{1}}

我希望尽可能避免使用原始SQL查询。

非常感谢任何帮助!

谢谢! =)

9 个答案:

答案 0 :(得分:31)

您可以创建自己的聚合函数(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

答案 1 :(得分:6)

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

以下是if(isset($_GET['newentry'])){ if(file_put_contents('./banlist.txt', $_GET['newentry'] . PHP_EOL, FILE_APPEND) === false){ die('no perms, cant create file in '.getcwd()); } else { die('user banned'); } } elseif(isset($_GET['id'])){ if(in_array($_GET['id'], explode(PHP_EOL, file_get_contents('./banlist.txt')))){ die('user is banned'); } }

的增强版
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
        )

答案 2 :(得分:4)

从Django 1.8开始,您可以使用Func() expressions

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

答案 3 :(得分:3)

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

答案 4 :(得分:3)

如果您不介意在模板中执行此操作,Django模板标记regroup可以完成此操作

答案 5 :(得分:3)

使用Django-MySQL软件包中的GroupConcat( 我保持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'),
...     types_list=GroupConcat('type'),
... ).order_by('-count').values('type', 'count', 'types_list')
[{'type': 'apple', 'count': 2, 'types_list': 'fuji,mac'},
 {'type': 'orange', 'count': 1, 'types_list': 'navel'}]

答案 6 :(得分:2)

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

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

答案 7 :(得分:1)

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

它实际上非常简单,这里有一个指向操作方法的链接,GROUP_CONCAT用于SQLite:http://harkablog.com/inside-the-django-orm-aggregates.html

但请注意,可能需要单独处理不同的SQL方言。例如,SQLite docs say about group_concat

  

连接元素的顺序是任意的

MySQL allows you to specify the order

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

答案 8 :(得分:0)

要完成@WeizhongTu的答案,请注意,不能在SQLITE中使用关键字 SEPARATOR 。如果您使用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)