Django: Filtering datetime field by *only* the year value?

Question:

I’m trying to spit out a django page which lists all entries by the year they were created. So, for example:

2010:

  • Note 4

  • Note 5

  • Note 6

2009:

  • Note 1

  • Note 2

  • Note 3

It’s proving more difficult than I would have expected. The model from which the data comes is below:

class Note(models.Model):
    business = models.ForeignKey(Business)
    note = models.TextField()
    created = models.DateTimeField(auto_now_add=True)
    updated = models.DateTimeField(auto_now=True)
    class Meta:
        db_table = 'client_note'

    @property
    def note_year(self):
        return self.created.strftime('%Y')

    def __unicode__(self):
        return '%s' % self.note

I’ve tried a few different ways, but seem to run into hurdles down every path. I’m guessing an effective ‘group by’ method would do the trick (PostGres DB Backend), but I can’t seem to find any Django functionality that supports it. I tried getting individual years from the database but I struggled to find a way of filtering datetime fields by just the year value. Finally, I tried adding the note_year @property but because it’s derived, I can’t filter those values.

Any suggestions for an elegant way to do this? I figure it should be pretty straightforward, but I’m having a heckuva time with it. Any ideas much appreciated.

Asked By: PlankTon

||

Answers:

You can use django.views.generic.date_based.archive_year or use year field lookup.

Answered By: Davor Lucic

Either construct custom SQL or use

date_list = Note.objects.all().dates('created', 'year')

for years in date_list:
    Note.objects.filter(created__year = years.year)

This is the way it is done in date based generic views.

Answered By: zovision

We can improve the filter using a list of years, then check if the year of creation is on that.

With that list, the database will be called once one time, because the Note.objects.filter will runs outside the for loop.

Something like this:

notes = Note.objects.all().dates('created', 'year')
years = [note.year for note in notes]

Note.objects.filter(created__year__in = years)