2009-12-15 2 views
5

Non ho davvero trovato il modo di tradurre GROUP BY e HAVING in Django QuerySet.annotate e QuerySet.aggregate. Sto cercando di tradurre questo query SQL in ORM parlanoCome eseguire questa query GROUP BY nell'ORM di Django con annotazione e aggregato

SELECT EXTRACT(year FROM pub_date) as year, EXTRACT(month from pub_date) as month, COUNT(*) as article_count FROM articles_article GROUP BY year,month; 

che Risulterà:

[(2008.0, 10.0, 1L), # year, month, number of articles 
(2009.0, 2.0, 1L), 
(2009.0, 7.0, 1L), 
(2008.0, 5.0, 3L), 
(2008.0, 9.0, 1L), 
(2008.0, 7.0, 1L), 
(2009.0, 5.0, 1L), 
(2008.0, 8.0, 1L), 
(2009.0, 12.0, 2L), 
(2009.0, 3.0, 1L), 
(2007.0, 12.0, 1L), 
(2008.0, 6.0, 1L), 
(2009.0, 4.0, 2L), 
(2008.0, 3.0, 1L)] 

Il mio modello Django:

class Article(models.Model): 
    title = models.CharField(max_length=150, verbose_name=_("title")) 
    # ... more 
    pub_date = models.DateTimeField(verbose_name=_('publishing date')) 

Questo progetto dovrebbe funzionare su un paio di diversa Sistemi DB, quindi sto cercando di stare lontano dal puro SQL il più possibile.

risposta

14

penso di farlo in una query potrebbe essere necessario avere il mese e l'anno come campi separati ...

Article.objects.values('pub_date').annotate(article_count=Count('title')) 

Che sarebbe group by da pub_date. Ma non c'è modo che io possa pensare di fare l'equivalente della clausola di funzione extract in linea lì.

Se il modello fosse:

class Article(models.Model): 
    title = models.CharField(max_length=150, verbose_name=_("title")) 
    # ... more 
    pub_date = models.DateTimeField(verbose_name=_('publishing date')) 
    pub_year = models.IntegerField() 
    pub_month = models.IntegerField() 

Poi si potrebbe fare:

Article.objects.values('pub_year', 'pub_month').annotate(article_count=Count('title')) 

Se avete intenzione di fare questo, consiglio avendo pub_year e pub_month essere popolato automaticamente sovrascrivendo il save() metodo per Articolo ed estrazione dei valori da pub_date.


Edit:

Un modo per farlo è quello di utilizzare extra; ma non vi concederà l'indipendenza del database ...

models.Issue.objects.extra(select={'year': "EXTRACT(year FROM pub_date)", 'month': "EXTRACT(month from pub_date)"}).values('year', 'month').annotate(Count('title')) 

Anche se questo funzionerà, credo (non testato), si richiede di modificare i campi extra se mai cambiare i server di database. Ad esempio, in SQL Server si dovrebbe fare extract(year from pub_date) ...

Questo potrebbe non essere così male se si crea un gestore di modello personalizzato che si contrassegna in modo evidente come richiedere tali modifiche dipendenti dal motore di database.

+0

bel trucco, grazie! Ho bisogno di questa query solo per una singola pagina, il che rende un po 'difficile giustificare due campi aggiuntivi. Ma usare 'extra' farà il lavoro per ora :) –

+0

L'esempio' extra' che hai postato funziona alla grande. Vorrei poter superare questa risposta di più! –