[Django]-Django, filter by specified month and year in date range

86👍

Check the documentation

year = 2012
month = 09
Departure_Date.objects.filter(date_from__year__gte=year,
                              date_from__month__gte=month,
                              date_to__year__lte=year,
                              date_to__month__lte=month)

Alternative method using .extra:

where = '%(year)s >= YEAR(date_from) AND %(month)s >= MONTH(date_from) \
        AND %(year)s <= YEAR(date_to) AND %(month)s <= MONTH(date_to)' % \
        {'year': year, 'month': month}
Departure_Date.objects.extra(where=[where])

There is a specific case where above query does not yield a desired result.

For example:

date_from='2012-11-01'
date_to='2013-03-17'
and input is
year=2013
month=1

Then %(month)s >= MONTH(date_from) condition is wrong because month 1 is < month 11 in date_from but year is different so MySQL IF condition is required here:

where = '%(year)s >= YEAR(date_from) AND IF(%(year)s > YEAR(date_from), \
     IF(%(month)s > MONTH(date_from), %(month)s >= MONTH(date_from), %(month)s < MONTH(date_from)), \
     IF(%(month)s < MONTH(date_from), %(month)s < MONTH(date_from), %(month)s >= MONTH(date_from))) \
     AND %(year)s <= YEAR(date_to) \
     AND %(month)s <= MONTH(date_to)' % \
     {'year': year, 'month': month}
Departure_Date.objects.extra(where=[where])

5👍

Solution using python code only. Main idea is to construct date_from and date_to with python. Then these dates can be used in filter with __lte and __gte:

import calendar
from datetime import datetime
from django.db.models import Q

def in_month_year(month, year):
    d_fmt = "{0:>02}.{1:>02}.{2}"
    date_from = datetime.strptime(
        d_fmt.format(1, month, year), '%d.%m.%Y').date()
    last_day_of_month = calendar.monthrange(year, month)[1]
    date_to = datetime.strptime(
        d_fmt.format(last_day_of_month, month, year), '%d.%m.%Y').date()
    return Departure_Date.objects.filter(
        Q(date_from__gte=date_from, date_from__lte=date_to)
         |
        Q(date_from__lt=date_from, date_to__gte=date_from))

Now this will work:

>>> Departure_Date.objects.all()
[<Departure_Date: id: 1 - from: 2012-11-01 - to: 2013-03-17>,
<Departure_Date: id: 2 - from: 2012-11-01 - to: 2012-12-16>,
<Departure_Date: id: 3 - from: 2012-09-16 - to: 2012-10-31>,
<Departure_Date: id: 4 - from: 2012-11-01 - to: 2012-12-16>,
<Departure_Date: id: 5 - from: 2013-01-04 - to: 2013-01-11>]


>>> in_month_year(month=1, year=2013)
[<Departure_Date: id: 1 - from: 2012-11-01 - to: 2013-03-17>,
<Departure_Date: id: 5 - from: 2013-01-04 - to: 2013-01-11>]
👤stalk

2👍

You can get around the “impedance mismatch” caused by the lack of precision in the DateTimeField/date object comparison — that can occur if using range — by using a datetime.timedelta to add a day to last date in the range. This works like:

import datetime

start = date(2012, 12, 11)
end = date(2012, 12, 18)
new_end = end + datetime.timedelta(days=1)

ExampleModel.objects.filter(some_datetime_field__range=[start, new_end])

1👍

You can use 2 filters with __gte and __lte:

import datetime as dt

start = dt.date(2012, 12, 11)
end = dt.date(2012, 12, 18)
ExampleModel.objects.filter(date_field__gte=start).filter(date_field__lte=end)

Leave a comment