2009-12-15 3 views
5

Je n'ai pas vraiment eu de problème pour traduire GROUP BY et HAVING en et QuerySet.aggregate de Django. Je suis en train de traduire cette requête SQL en ORM parlerComment faire cette requête GROUP BY dans l'ORM de Django avec annoter et agréger

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; 

qui sort ceci:

[(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)] 

Mon modèle Django:

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

Ce projet devrait fonctionner sur deux différents Systèmes DB, donc j'essaie de rester loin de SQL pur autant que possible.

Répondre

14

Je pense que de le faire dans une requête que vous pourriez avoir le mois et l'année en tant que champs séparés ...

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

Ce serait group by par pub_date. Mais il n'y a aucun moyen de penser à faire l'équivalent de la clause de fonction extract ici.

Si votre modèle était:

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

Ensuite, vous pouvez faire:

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

Si vous allez faire cela, je recommande d'avoir pub_year et pub_month automatiquement renseignés en redéfinissant la save() méthode pour l'article et l'extraction des valeurs de pub_date.


Edit:

Une façon de le faire est d'utiliser extra; mais il ne sera pas Même si cela fonctionnera vous accorder l'indépendance de base de données ...

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

, je pense (non testé), il vous demandera de modifier les champs extra si vous modifiez jamais les serveurs de base de données. Par exemple, dans SQL Server vous feriez year(pub_date) au lieu de extract(year from pub_date) ...

Cela pourrait ne pas être si mauvais si vous venez avec un gestionnaire de modèle personnalisé que vous marquez de manière évidente comme nécessitant de telles modifications dépendantes du moteur de base de données.

+0

trick Nice, merci! J'ai besoin de cette requête seulement pour une seule page, ce qui rend un peu difficile de justifier deux champs supplémentaires. Mais utiliser 'extra 'fera l'affaire pour l'instant :) –

+0

L'exemple' extra' que vous avez posté fonctionne très bien. Je souhaite que je pourrais upvote cette réponse plus! –

Questions connexes