14

I have a model that contains datefield. I'm trying to get query set of that model that contains current week (starts on Monday).

So since Django datefield contains simple datetime.date model I assumed to filter by using .isocalendar(). Logically it's exactly what I want without no extra comparisons and calculations by current week day.

So what I want to do essentially is force .filter statement to behave in this logic:

if model.date.isocalendar()[2] == datetime.date.today().isocalendar()[2]
    ...

Yet how to write it inside filter statement? .filter(model__date__isocalendar=datetime.date.today().isocalendar()) will give wrong results (same as comparing to today not this week).

As digging true http://docs.python.org/library/datetime.html I have not noticed any other week day options...

Note from documentation:

date.isocalendar() Return a 3-tuple, (ISO year, ISO week number, ISO weekday).

Update:

Although I disliked the solution of using ranges yet it's the best option. However in my case I made a variable that marks the beginning of the week and just look greater or equal value because if I'm looking for a matches for current week. In case of giving the number of the week It would require both ends.

today = datetime.date.today()
monday = today - datetime.timedelta(days=today.weekday())

... \
.filter(date__gte=monday)
JackLeo
  • 4,579
  • 9
  • 40
  • 66
  • I don't know if the orm support that but you can fallback to raw query if it doesn't https://docs.djangoproject.com/en/dev/topics/db/sql/ – dm03514 May 09 '12 at 14:26
  • I'm aware of this option dm03514, but I would rather write my own filter then. That is also an option yet I'm asking to know maybe it is possible to achieve this using default actions in Django without extending it. – JackLeo May 09 '12 at 14:30
  • 1
    btw there is nice python dateutil module that might be helpful http://pypi.python.org/pypi/python-dateutil – aisbaa May 09 '12 at 14:39

4 Answers4

9

You're not going to be able to do this. Remember it's not just an issue of what Python supports, Django has to communicate the filter to the database, and the database doesn't support such complex date calculations. You can use __range, though, with a start date and end date.

Chris Pratt
  • 232,153
  • 36
  • 385
  • 444
  • Yes, I also would use *__range*, maybe event add custom ModelManager and custom Queryset with *week(date_field=(2012, 18))* method. – aisbaa May 09 '12 at 14:37
  • I know about range option, yet I was avoiding it due to action count that is required to get both ends of the week, but I guess that may be the lesser evil. Thanks non the less. – JackLeo May 09 '12 at 14:37
  • 1
    Yeah, not every part of your app needs to be code golf eligible. Sometimes you have to take a longer path. Always do what is most simple, most obvious, and creates the least footprint (modifying how Django handles filters would leave a pretty huge footprint). – Chris Pratt May 09 '12 at 14:46
  • Although I did not used __range, as for my general question it is the best solution. Thanks for the help. – JackLeo May 09 '12 at 15:28
6

Even simpler than using Extract function that Amit mentioned in his answer is using __week field lookup added in Django 1.11, so you can simply do:

.filter(model__date__week=datetime.date.today().isocalendar()[1])
radoh
  • 4,554
  • 5
  • 30
  • 45
  • 2
    Back in the day this was not possible, but this should now be the accepted answer! –  Aug 15 '19 at 08:15
2

ExtractWeek has been introduced in Django 1.11 for filtering based on isoweek number.

For Django 1.10 and lower versions, following solution works for filtering by iso number week on postgres database:

from django.db.models.functions import Extract
from django.db import models
@models.DateTimeField.register_lookup
class ExtractWeek(Extract):
    lookup_name = 'week'

Now do query as follows

queryset.annotate(week=ExtractWeek('date'))\
            .filter(week=week_number)
Amit Jaiswal
  • 985
  • 1
  • 9
  • 16
1

(This answer should only work for postgres, but might work for other databases.)

A quick and elegant solution for this problem would be to define these two custom transformers:

from django.db import models
from django.db.models.lookups import DateTransform

@models.DateTimeField.register_lookup
class WeekTransform(DateTransform):
    lookup_name = 'week'


@models.DateTimeField.register_lookup
class ISOYearTransform(DateTransform):
    lookup_name = 'isoyear'

Now you can query by week like this:

from django.utils.timezone import now
year, week, _ = now().isocalendar()

MyModel.objects.filter(created__isoyear=year, created__week=week)

Behinds the scenes, the Django DateTransform object uses the postgres EXTRACT function, which supports week and isoyear.

Yotam Ofek
  • 2,300
  • 1
  • 18
  • 21