Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django, filter by specified month and year in date range

Tags:

python

django

I have the following models

class Destination_Deal(models.Model):
    name = models.CharField(_("Nombre"),max_length=200)

class Departure_Date(models.Model):
    date_from= models.DateField(_('Desde'))    
    date_to= models.DateField(_('Hasta'))
    destination_deal = models.ForeignKey(Destination_Deal,verbose_name = _("Oferta de Destino"))

This is the actual data in the table departure_date

id  date_from   date_to     destination_deal_id
1   2012-11-01  2013-03-17  1
2   2012-11-01  2012-12-16  2
3   2012-09-16  2012-10-31  3
4   2012-11-01  2012-12-16  3
5   2013-01-04  2013-01-11  4

I would like to filter the Destination_Deals if a specified month&year is between date_from and date_to.

Example 1

Month: September (09)
Year: 2012

Wanted departure dates result:
ID 3 : It is the only data range that touch 09/2012

Example 2

Month: February (02)
Year: 2013

Wanted departure dates result:
ID 1 : 02/2012 is before 03/2012

So, the day actually is does not matter. If the month&year is between date_from and date_to, even if it is by one day it must be filter.

I think I must use something like this but I am not sure how to do it.

Thanks in advance! Miguel

---Edit---
This is the test for the answer from Aamir Adnan but it is not working as I expected as ID 1 must be also returned because it goes from November 2012 to March 2013, so January 2013 is between.

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>]


month:1
year:2013
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])
[<Departure_Date: id: 5 - from: 2013-01-04 - to: 2013-01-11>]
like image 984
Miguel Febres Avatar asked Dec 29 '12 01:12

Miguel Febres


People also ask

How do I filter query objects by date range in Django?

To filter query objects by date range in Python Django, we can use the filter method. to call objects. filter with the date__range parameter set to a list with the date range with the strings as the date values. date is the column name we're filtering by.

How can I filter a Django query with a list of values?

To filter a Python Django query with a list of values, we can use the filter method with in . to search Blog entries with pk set to 1,4 or 7 by calling Blog. objects. filter with the pk_in argument set to [1, 4, 7] .

How do I filter records in Django?

The filter() method is used to filter you search, and allows you to return only the rows that matches the search term.

What does .values do in Django?

Django values_list() is an optimization to grab specific data from the database instead of building and loading the entire model instance.


3 Answers

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])
like image 162
Aamir Rind Avatar answered Oct 12 '22 01:10

Aamir Rind


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>]
like image 31
stalk Avatar answered Oct 12 '22 02:10

stalk


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])
like image 2
Alvaro Joao Avatar answered Oct 12 '22 01:10

Alvaro Joao