2010-08-22 25 views
15

tengo un modelo muy básico:Django anotaciones agrupaciones por mes

class Link(models.Model): 
    title = models.CharField(max_length=250, null=False) 
    user = models.ForeignKey(User) 
    url = models.CharField(max_length=250, blank=True, null=True) 
    link_count = models.IntegerField(default=0) 
    pub_date = models.DateField(auto_now_add=True) 
    updated = models.DateTimeField(auto_now=True) 

puedo crear una lista de todas las entradas agrupadas por fecha usando:

Link.objects.values('pub_date').order_by('-pub_date').annotate(dcount=Count('pub_date')) 

Esto, naturalmente, agrupar los elementos por día . Pero lo que realmente quiero hacer es grupo por mes. ¿Hay alguna forma de que pueda hacer esto usando anotar()?

Muchas gracias,

G

+0

una respuesta, pero es posible tener una apariencia en este billete: http://code.djangoproject.com/ticket/10302 –

+0

lo que es 'Count'? –

Respuesta

15

Si estás en PostgreSQL, el siguiente podría funcionar:

from django.db.models import Count 

Link.objects.extra(select={'month': 'extract(month from pub_date)'}).values('month').annotate(dcount=Count('pub_date')) 

no estoy seguro de cómo portátil extract es a través de otras bases de datos.

+0

Gracias! Esto parece funcionar también para mySQL. Excepto esto agrupa todos los meses en cualquier año. Estoy tratando de agruparme meses por cada año. Pero este es un gran comienzo, así que retocaré. Gracias de nuevo. – givp

+0

puede abofetear un filtro que limita las fechas a ciertos rangos. '.filter (pub_date__gte = datetime.datetime (complete la fecha aquí))' – boatcoder

+4

usando 'connection.ops.date_trunc_sql ('month', 'date')' en lugar del extracto codificado SQL haría la declaración compatible con otros backends – jujule

13
from django.db import connections 
from django.db.models import Count 

Link.objects.extra(select={'month': connections[Link.objects.db].ops.date_trunc_sql('month', 'pub_date')}).values('month').annotate(dcount=Count('pub_date')) 
+1

Note que también debe importar 'de django.db.models import Count' –

3

Para añadir, como una alternativa para el uso de extra(): desde Django 1.8, también se puede usar expresiones condicionales.

>>> year_overview = Link.objects.filter(pub_date__year=year).aggregate(
    jan=Sum(
     Case(When(created__month=0, then=1), 
      output_field=IntegerField()) 
    ), 
    feb=Sum(
     Case(When(created__month=1, then=1), 
      output_field=IntegerField()) 
    ), 
    mar=Sum(
     Case(When(created__month=2, then=1), 
      output_field=IntegerField()) 
    ), 
    apr=Sum(
     Case(When(created__month=3, then=1), 
      output_field=IntegerField()) 
    ), 
    may=Sum(
     Case(When(created__month=4, then=1), 
      output_field=IntegerField()) 
    ), 
    jun=Sum(
     Case(When(created__month=5, then=1), 
      output_field=IntegerField()) 
    ), 
    jul=Sum(
     Case(When(created__month=6, then=1), 
      output_field=IntegerField()) 
    ), 
    aug=Sum(
     Case(When(created__month=7, then=1), 
      output_field=IntegerField()) 
    ), 
    sep=Sum(
     Case(When(created__month=8, then=1), 
      output_field=IntegerField()) 
    ), 
    oct=Sum(
     Case(When(created__month=9, then=1), 
      output_field=IntegerField()) 
    ), 
    nov=Sum(
     Case(When(created__month=10, then=1), 
      output_field=IntegerField()) 
    ), 
    dec=Sum(
     Case(When(created__month=11, then=1), 
      output_field=IntegerField()) 
    ), 
) 

>>> year_overview 
{'mar': None, 'feb': None, 'aug': None, 'sep': 95, 'apr': 1, 'jun': None, 'jul': None, 'jan': None, 'may': None, 'nov': 87, 'dec': 94, 'oct': 100} 
+2

Con la última versión de Django supongo que esta es la solución recomendada –

2

He leído que .extra() quedará obsoleto en el futuro. Sugieren usar en su lugar objetos Func. Y hay uno para extraer un mes sin usar una declaración Case dolorosa.

from django.db.models.functions import ExtractMonth 
Link.objects.all().annotate(pub_date_month=ExtractMonth('pub_date')) 
No