2012-04-26 21 views
12

że mam poniższej tabeli nazywa fruits:GROUP_CONCAT równowartość w Django

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

Moim celem jest, aby w końcu wymyślić zliczania różnych types oraz listę oddzielonych przecinkami w names:

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

Można to łatwo zrobić za pomocą GROUP_CONCAT w MySQL, ale mam problem z odpowiednikiem Django. To, co mam tak daleko, ale jestem brakuje GROUP_CONCAT rzeczy:

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

Chciałbym unikać surowych zapytań SQL, jeśli to możliwe.

Każda pomoc będzie bardzo ceniona!

Dzięki! =)

Odpowiedz

3

ORM Django nie obsługuje tego; jeśli nie chcesz używać surowego SQL, musisz group and join.

+1

Kolega twierdzi projektu open source, która udostępnia funkcje specyficzne mysql jak GROUP_CONCAT w Django. Zobacz https://github.com/adamchainz/django-mysql/ –

3

Jeśli nie przeszkadza mi to w szablonie Django szablon tag regroup realizuje to

1

nie są obsługiwane przez Django ORM, ale można zbudować własny agregatora.

To rzeczywiście bardzo proste, tu jest link do how-to, że właśnie to robi, z GROUP_CONCAT SQLite: http://harkablog.com/inside-the-django-orm-aggregates.html

Uwaga jednak, że może być konieczne do obsługi różnych dialektów SQL oddzielnie. Na przykład, SQLite docs say about group_concat:

Kolejność łączonych elementów jest dowolna

Podczas MySQL allows you to specify the order.

Myślę, że to może być powód, dla którego GROUP_CONCAT nie jest w tej chwili zaimplementowany w Django.

22

Można tworzyć własne funkcji zbiorczej (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) 

i używać go po prostu jako:

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

używam Django 1.8 i MySQL 4.0.3

+0

To jest niesamowite! Działa również na SQLLite! –

+1

UWAGA, że Django (> = 1.8) dostarcza ['Funkcje bazy danych'] (http://stackoverflow.com/a/40478702/2714931) – WeizhongTu

3

dzień Django 1.8 można użyć Func() expressions.

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

Zastosowanie GroupConcat pakiecie Django MySQL ( https://django-mysql.readthedocs.org/en/latest/aggregates.html#django_mysql.models.GroupConcat), które utrzymują. Dzięki niemu można zrobić to po prostu lubię:

>>> 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'}] 
5

Zauważ, że Django (> = 1.8) zapewnia Database functions wsparcie. https://docs.djangoproject.com/en/dev/ref/models/database-functions/#concat

Oto udoskonalona wersja 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 
     ) 

Wykorzystanie:

LogModel.objects.values('level', 'info').annotate(
    count=Count(1), time=GroupConcat('time', ordering='time DESC', separator=' | ') 
).order_by('-time', '-count') 
+0

To nie działa; t działa dla mnie. Kolejny dobry przykład: https://gist.github.com/ludoo/ca6ed07e5c8017272701 –

+0

@ Iliaw495Nikitin to działa dobrze w moim projekcie przy użyciu Django 1.10.x – WeizhongTu

+0

Działa również w Django 1.11.x. Dzięki! – d345k0