如何在Django中按日期范围过滤查询对象?


248

我在一个模型中有一个领域,例如:

class Sample(models.Model):
    date = fields.DateField(auto_now=False)

现在,我需要按日期范围过滤对象。

如何过滤日期在1-Jan-2011和之间的所有对象31-Jan-2011

Answers:


409

Sample.objects.filter(date__range=["2011-01-01", "2011-01-31"])

或者,如果您只是想按月过滤:

Sample.objects.filter(date__year='2011', 
                      date__month='01')

编辑

正如伯恩哈德Vallant说,如果你想查询集去掉了specified range ends,你应该考虑自己的解决方案,它采用GT / LT(大于/小于号)。


date1的数据类型是什么?我现在有datetime对象。
user469652 2011年

8
@dcordjer:应该另外说说__range包括边界(如sql的边界BETWEEN),如果您不希望包括边界,则必须使用我的gt / lt解决方案……
Bernhard Vallant 2011年

这是固有地以任何顺序排序的吗?如果是这样,哪个命令?谢谢。
理查德·邓恩

1
@RichardDunn排序将基于模型的默认排序,或者是否使用order_by上述生成QuerySetfilter。我多年没有使用Django。
crodjer

对于date__range,您需要输入下个月的01。这是指向exmaplins的文档的链接,该文档可以转换为日期的00:00:00.0000,因此不包括您范围内的最后一天。docs.djangoproject.com/en/1.10/ref/models/querysets/#range 在这种情况下,我使用:date__range = [“%s-%s-1”%(year,month),“%s-%s- 1“%(year,int(month)+1)]
SpiRail


79

使用过滤器进行Django范围设置时,请确保您知道使用日期对象与日期时间对象之间的区别。__range是日期中包含的内容,但是如果您使用datetime对象作为结束日期,则如果未设置时间,它将不包括该天的条目。

    startdate = date.today()
    enddate = startdate + timedelta(days=6)
    Sample.objects.filter(date__range=[startdate, enddate])

返回从开始日期到结束日期的所有条目,包括那些日期的条目。不好的例子,因为这将在未来一周返回条目,但您会遇到麻烦。

    startdate = datetime.today()
    enddate = startdate + timedelta(days=6)
    Sample.objects.filter(date__range=[startdate, enddate])

根据日期字段的设置时间,将缺少24小时的输入值。


5
我认为重要的是要注意如何导入date对象: >>> from datetime import date >>> startdate = date.today()
Alex Spencer

19

通过使用datetime.timedelta在范围中的最后一个日期添加日期,可以避免由于DateTimeField/date对象比较精度不足而导致的“阻抗不匹配”(如果使用范围,则可能发生)。其工作原理如下:

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

如前所述,如果不这样做,记录将在最后一天被忽略。

进行编辑以避免使用datetime.combine-与a进行比较时,坚持日期实例似乎更合乎逻辑DateTimeField,而不是乱扔掉(且容易混淆)datetime对象。请参阅下面的注释中的进一步说明。


1
有与截断方法简化了这个一个真棒德洛雷安库:delorean.readthedocs.org/en/latest/quickstart.html#truncation
trojjer

@tojjer:看起来很有希望,但是我们如何在这里使用truncate方法?
尤金2014年

@eugene:在所有这些月之后,我刚刚再次探讨了这一点,您是对的,因为毕竟这对这种情况并没有真正的帮助。我能想到的唯一解决方法是在我的原始响应中建议,这是在过滤日期实例时提供额外的“填充”以便与日期时间模型字段进行比较。这可以通过上述的datetime.combine方法完成,但是我发现,通过在范围内的开始/结束日期中添加timedelta(days = 1)来仅解决差异可能会更简单- -根据问题。
trojjer 2014年

因此,Example.objects.filter(created__range=[date(2014, 1, 1), date(2014, 2, 1)])将不会包括在上创建的对象date(2014, 2, 1),如@cademan有用地解释。但是,如果您增加一天来增加结束日期,则会得到一个查询集,其中涵盖了那些丢失的对象(并且date(2014, 2, 2)由于相同的怪癖而方便地省略了创建的对象)。这里令人烦恼的是,使用指定的“手动”范围created__gte ... created__lte=date(2014, 2, 1)也不起作用,这绝对是违反直觉的恕我直言。
trojjer 2014年

1
@tojjer:datetime_field__range = [delorean.parse('2014-01-01')。date,delorean.parse('2014-02-01')。date]对我
有用


1

为了使其更加灵活,可以设计如下的FilterBackend:

class AnalyticsFilterBackend(generic_filters.BaseFilterBackend):
    def filter_queryset(self, request, queryset, view):
        predicate = request.query_params # or request.data for POST

        if predicate.get('from_date', None) is not None and predicate.get('to_date', None) is not None:
            queryset = queryset.filter(your_date__range=(predicate['from_date'], predicate['to_date']))

        if predicate.get('from_date', None) is not None and predicate.get('to_date', None) is None:
            queryset = queryset.filter(your_date__gte=predicate['from_date'])

        if predicate.get('to_date', None) is not None and predicate.get('from_date', None) is None:
            queryset = queryset.filter(your_date__lte=predicate['to_date'])
        return queryset

-2

今天仍然有意义。您也可以这样做:

import dateutil
import pytz

date = dateutil.parser.parse('02/11/2019').replace(tzinfo=pytz.UTC)
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.