2012-04-26 23 views
12

decir que tengo la siguiente tabla llamada fruits:GROUP_CONCAT equivalente en Django

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

Mi objetivo es llegar en última instancia, con un recuento de los distintos types y una lista separada por comas de la names:

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

Esto se puede hacer fácilmente con GROUP_CONCAT en MySQL, pero estoy teniendo problemas con el equivalente de Django. Esto es lo que tengo hasta ahora, pero me falta la materia GROUP_CONCAT:

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

me gustaría evitar el uso de consultas SQL primas si es posible.

¡Cualquier ayuda sería muy apreciada!

Gracias! =)

Respuesta

3

El Django ORM no es compatible con esto; si no desea utilizar SQL sin formato, necesitará group and join.

+1

Un colega mío mantiene un proyecto de código abierto que expone funciones específicas de MySQL como GROUP_CONCAT en Django. Eche un vistazo https://github.com/adamchainz/django-mysql/ –

3

Si no le importa hacer esto en la plantilla de la etiqueta de plantillas de Django regroup logra esto

1

No es compatible con ORM de Django, pero usted puede construir su propio agregador.

En realidad es bastante sencillo, aquí hay un enlace a un how-to que hace precisamente eso, con GROUP_CONCAT para SQLite: http://harkablog.com/inside-the-django-orm-aggregates.html

Nota sin embargo, que podría ser necesario para manejar diferentes dialectos SQL por separado. Por ejemplo, el SQLite docs say about group_concat:

El orden de los elementos concatenados es arbitrario

Mientras MySQL allows you to specify the order.

Supongo que puede ser una razón por la cual GROUP_CONCAT no está implementado en Django en este momento.

22

Usted puede crear su propia función de agregado (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) 

y utilizarla simplemente como:

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

estoy usando Django 1.8 y MySQL 4.0.3

+0

¡Esto es asombroso! ¡Y también funciona en SQLLite! –

+1

AVISO de que Django (> = 1.8) proporciona ['Funciones de base de datos'] (http://stackoverflow.com/a/40478702/2714931) – WeizhongTu

3

A partir de Django 1.8 puede usar Func() expressions.

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

Uso GroupConcat del paquete de Django-MySQL ( https://django-mysql.readthedocs.org/en/latest/aggregates.html#django_mysql.models.GroupConcat), que mantengo. Con él puede hacerlo simplemente desea:

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

AVISO que Django (> = 1.8) proporciona Database functions apoyo. https://docs.djangoproject.com/en/dev/ref/models/database-functions/#concat

Aquí es una versión mejorada de 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 
     ) 

Uso:

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

No funciona para mí. Otro buen ejemplo: https://gist.github.com/ludoo/ca6ed07e5c8017272701 –

+0

@ Iliaw495Nikitin esto funciona bien en mi proyecto usando Django 1.10.x – WeizhongTu

+0

Funciona bien en Django 1.11.x. ¡Gracias! – d345k0