2012-04-26 18 views
12

sagen, ich habe die folgende Tabelle fruits genannt:GROUP_CONCAT Äquivalent in Django

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

Mein Ziel ist es letztlich mit einer Zählung der verschiedenen types und eine kommagetrennte Liste der names kommen:

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

Dies kann leicht mit GROUP_CONCAT in MySQL getan werden, aber ich habe Probleme mit dem Django-Äquivalent. Das ist, was ich habe bisher aber ich fehle das GROUP_CONCAT Zeug:

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

ich, wenn möglich, mit rohen SQL-Abfragen vermeiden möchte.

Jede Hilfe würde sehr geschätzt werden!

Danke! =)

Antwort

3

Das Django ORM unterstützt dies nicht; Wenn Sie kein Raw-SQL verwenden möchten, müssen Sie group and join.

+1

Ein Kollege von mir unterhält ein Open-Source-Projekt, die mysql spezifische Funktionen wie GROUP_CONCAT in django aussetzt. Werfen Sie einen Blick auf https://github.com/adamchainz/django-mysql/ –

3

Wenn Sie nicht tun dies in der Vorlage etwas dagegen, die Django Template-Tag regroup diese von Django ORM

1

Nicht unterstützt erreicht, aber Sie können Ihre eigenen Aggregator bauen.

Es ist eigentlich ziemlich einfach, hier ist ein Link zu einem wie-zu, dass genau das tut, mit GROUP_CONCAT für SQLite: http://harkablog.com/inside-the-django-orm-aggregates.html

ist jedoch zu beachten, dass es notwendig sein könnte separat verschiedene SQL-Dialekte zu behandeln. Zum Beispiel kann die SQLite docs say about group_concat:

Die Reihenfolge der verketteten Elemente ist beliebig

Während MySQL allows you to specify the order.

Ich denke, das kann ein Grund sein, warum GROUP_CONCAT es in Django im Moment nicht implementiert ist.

22

Sie können Ihre eigene Aggregate Function (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) 

und verwenden Sie es einfach als erstellen:

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

Ich bin mit django 1.8 und MySQL 4.0.3

+0

Das ist erstaunlich! Und es funktioniert auch auf SQLLite! –

+1

HINWEIS, dass Django (> = 1.8) bietet ['Datenbankfunktionen'] (http://stackoverflow.com/a/40478702/2714931) – WeizhongTu

3

Ab Django 1.8 können Sie Func() expressions verwenden.

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

Verwendung GroupConcat vom Django-MySQL Paket ( https://django-mysql.readthedocs.org/en/latest/aggregates.html#django_mysql.models.GroupConcat), die ich halten. Mit ihm können Sie es einfach tun mag:

>>> 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

Beachten Sie, dass Django (> = 1.8) Database functions unterstützt.Hier https://docs.djangoproject.com/en/dev/ref/models/database-functions/#concat

ist eine erweiterte Version von 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 
     ) 

Verbrauch:

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

Es funktioniert nicht für mich. Ein anderes schönes Beispiel: https://gist.github.com/ludoo/ca6ed07e5c8017272701 –

+0

@ Iliaw495Nikitin dies funktioniert gut in meinem Projekt mit Django 1.10.x – WeizhongTu

+0

Funktioniert gut in Django 1.11.x. Vielen Dank! – d345k0