views:

33

answers:

3

Hello, I am trying use the django ORM to get a list by year of all my articles with an article count beside it, such as this:

2010 (5 articles)
2009 (4 articles)
2008 (9 articles)

I have tried things such as:

archive = Articles.objects.dates('created', 'year').annotate(archive_count=Count('created'))

or:

archive = Articles.objects.values('created').annotate(archive_count=Count('created'))

or:

archive = Articles.objects.values('created').aggregate(archive_count=Count('created'))

The last one gave me the right count but didn't give me any of the year values, the other ones give a mix of either nothing or archive_count being set to 1 for each row.

Any ideas where I am going wrong?

A: 

I'm not sure if keeping database hits to a minimum is your chief goal. If so, there may be another way that I haven't considered. But at first glance, this looks like what you want:

archive={}
years = Article.objects.dates('created', 'year')
for year in years:
    archive[year.year] = Article.objects.filter(created__year=year.year).count()

Then you'll have a dictionary with {2010: 5, 2009: 4, 2008: 9}.

Justin Myles Holmes
Something like this could work but I was trying to keep it efficient, and someone was able to create a solution with one database call.
Kevin
Yeah my hat is off to @OmerGertel's solution. It has mine beat hands down. I voted it up. :-)
Justin Myles Holmes
+1  A: 

In an ideal world, you would be able to write:

archive = Articles.objects.values('created__year').annotate(archive_count=Count('created')).order_by()

to get your desired results. Unfortunately django doesn't support anything other than exact field names as an arguments to values()

archive = Articles.objects.values('created').aggregate(archive_count=Count('created'))

can't possibly work since values('created') gives you a dictionary of all unique values for 'created', which is composed of more than just 'year'.

If you really want to do this with a single ORM call, you'll have to use the extra function and write entirely custom SQL. Otherwise Justin's answer should work well.

tarequeh
Yes I tried the first way hoping that it would work, to me that would be the ideal way to do it, but as you pointed out, it doesn't work.
Kevin
+2  A: 

Here's a way to do it in one query:

Article.objects.extra(select={'year':"strftime('%%Y',created)"}).values('year').order_by().annotate(Count('id'))

Note that you'll have to replace strftime('%%Y',created) according to your database (I was using sqlite).

OmerGertel
Thank you! That worked excellently, I am also using sqlite right now for development so it worked straight away.
Kevin