Подтвердить что ты не робот

Эквивалент GROUP_CONCAT в Django

Скажем, у меня есть следующая таблица под названием 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-запросов, если это возможно.

Любая помощь будет принята с благодарностью!

Спасибо! =)

4b9b3361

Ответ 2

Вы можете создать свою собственную функцию агрегации (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

Ответ 3

УВЕДОМЛЕНИЕ о том, что 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')

Ответ 4

Если вы не возражаете сделать это в своем шаблоне, тег шаблона Django regroup выполняет этот

Ответ 5

По Django 1.8 вы можете использовать выражения Func().

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

Ответ 6

Используйте 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'),
...     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'}]

Ответ 7

Не поддерживается Django ORM, но вы можете создать собственный агрегатор.

На самом деле это довольно просто, вот ссылка на руководство, которое делает именно это, с GROUP_CONCAT для SQLite: http://harkablog.com/inside-the-django-orm-aggregates.html

Обратите внимание, что, возможно, потребуется отдельно обрабатывать разные диалекты SQL. Например, SQLite docs говорят о GROUP_CONCAT:

Порядок конкатенированных элементов произвольный

Пока MySQL позволяет указать порядок.

Я предполагаю, что это может быть причиной того, что GROUP_CONCAT он не реализован в Django на данный момент.