Django,在日期范围内按指定月份和年份筛选

62

我有以下模型

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"))

这是表格 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

我想筛选出目的地交易,如果指定的月份和年份在date_from和date_to之间。

示例1

月份:九月(09)
年份:2012

所需出发日期结果:
ID 3:它是唯一一个接触到09/2012的数据范围。

示例2

月份:二月(02)
年份:2013

所需出发日期结果:
ID 1:02/2012在03/2012之前。

因此,实际上并不重要的是日期。如果月份和年份在date_from和date_to之间,即使是一天,也必须进行筛选。

我认为我必须使用类似这样的东西,但我不确定如何做。

---编辑---
这是对Aamir Adnan答案的测试,但它不像预期那样工作,因为ID 1也必须被返回,因为它从2012年11月到2013年3月,所以2013年1月之间。

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>]
4个回答

86

查看文档

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)

使用.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])

有一种特殊情况,以上查询结果不会产生期望的结果。

例如:

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

那么条件%(month)s >= MONTH(date_from)是错误的,因为在date_from中月份1小于月份11,但年份不同,因此需要使用MySQL的IF条件:

IF(MONTH(date_from) < %(month)s, YEAR(date_from) = %(year)s - 1, YEAR(date_from) = %(year)s)
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])

3
我收到了以下错误信息:“FieldError: 不允许在字段'date_to'上连接。 你错拼了查找类型的'month'吗?” 据我所知,混合后缀是不可能的,或者我错了? - Miguel Febres
现在我得到了 "DatabaseError: (1064, "您的SQL语法存在错误;请检查对应于您的MySQL服务器版本的手动操作指南,确保使用正确的语法,例如'MONTH(date_from)> = 12 YEAR(date_to)<= 2012 MONTH(date_to)< = 12LIMIT 21'在第1行")"。是否有其他方法可以使其与数据库无关,并且使用Destination_Deals而不是Departure_Date? - Miguel Febres
谢谢。我已经尝试过使用 "and",但那不是我要求的。你有没有尝试过使用示例数据?另一个例子,月份:1 - 年份:2013。结果应该是ID 1和ID 5。如果可能的话,我也想避免使用额外的if语句。 - Miguel Febres
请再次测试并告诉我它是否没有按照您的要求执行。我已更新查询,并在您的数据上进行了测试,一切正常。请再次复制我发布的查询。 - Aamir Rind
@AamirAdnan 我认为你把“lte”和“get”放错了地方。 - Avinash Raj
显示剩余4条评论

5

使用Python代码解决方案。主要思路是使用Python构建date_from和date_to日期。然后可以在filter中使用__lte__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))

现在这个会起作用:
>>> 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>]

2
您可以通过使用 datetime.timedelta 将一天添加到范围内的最后日期来解决由于 DateTimeField/date 对象比较的精度不足而引起的“阻抗失配”问题。这样就可以避免在使用 range 时出现问题。具体操作如下:
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

您可以使用__gte__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)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接