2012-04-26 16 views
12

dire che ho la seguente tabella denominata fruits:group_concat equivalente in Django

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

Il mio obiettivo è quello di venire in ultima analisi, con un conteggio del diverso types e un elenco delimitato da virgole del names:

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

Questo può essere fatto facilmente con GROUP_CONCAT in MySQL ma ho problemi con l'equivalente di Django. Questo è ciò che ho finora, ma mi manca la roba GROUP_CONCAT:

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

vorrei evitare di usare query SQL prime, se possibile.

Qualsiasi aiuto sarebbe molto apprezzato!

Grazie! =)

risposta

3

L'ORM di Django non supporta questo; se non si desidera utilizzare SQL raw, sarà necessario specificare group and join.

+1

Un mio collega mantiene un progetto open source che espone le funzioni specifiche di MySQL come group_concat in Django. Date un'occhiata https://github.com/adamchainz/django-mysql/ –

3

Se non ti dispiace fare questo nel modello template tag Django regroup realizza questo

1

Non supportato da Django ORM, ma si può costruire il proprio aggregatore.

In realtà è piuttosto semplice, ecco un link per un how-to che fa proprio questo, con GROUP_CONCAT per SQLite: http://harkablog.com/inside-the-django-orm-aggregates.html

Nota però, che potrebbe essere necessario per gestire diversi dialetti SQL separatamente. Ad esempio, il SQLite docs say about group_concat:

L'ordine degli elementi concatenati è arbitraria

Mentre MySQL allows you to specify the order.

Immagino che possa essere un motivo per cui GROUP_CONCAT non è implementato in Django al momento.

22

È possibile creare la propria funzione Aggregate (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) 

e usarlo semplicemente come:

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

Sto usando Django 1.8 e MySQL 4.0.3

+0

Questo è fantastico! E funziona anche su SQLLite! –

+1

AVVISO che Django (> = 1.8) fornisce ['Funzioni database'] (http://stackoverflow.com/a/40478702/2714931) – WeizhongTu

3

partire dal Django 1.8 puoi usare Func() expressions.

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

Usa GroupConcat dal pacchetto Django-MySQL ( https://django-mysql.readthedocs.org/en/latest/aggregates.html#django_mysql.models.GroupConcat), che io sostengo. Con essa è possibile farlo come semplice:

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

notare che Django (> = 1.8) fornisce Database functions supporto. https://docs.djangoproject.com/en/dev/ref/models/database-functions/#concat

Ecco una versione migliorata del 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 
     ) 

Usage:

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

Non funziona per me. Un altro buon esempio: https://gist.github.com/ludoo/ca6ed07e5c8017272701 –

+0

@ Iliaw495Nikitin questo funziona bene nel mio progetto utilizzando Django 1.10.x – WeizhongTu

+0

Funziona bene in Django 1.11.x. Grazie! – d345k0