Как отфильтровать объекты запросов по диапазону дат в Django?

248

У меня есть поле в одной модели, как:

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

Теперь мне нужно отфильтровать объекты по диапазону дат.

Как мне отфильтровать все объекты, у которых есть дата между 1-Jan-2011и 31-Jan-2011?

user469652
источник

Ответы:

411

использование

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

Или, если вы просто пытаетесь фильтровать по месяцам:

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

редактировать

Как сказал Бернхард Валлант, если вам нужен набор запросов, который исключает, specified range endsвы должны рассмотреть его решение , которое использует gt / lt (больше / меньше чем).

crodjer
источник
Каков тип данных date1? У меня сейчас объект datetime.
user469652
8
@dcordjer: следует добавить, что он __rangeвключает в себя границы (например, sql BETWEEN), если вы не хотите, чтобы границы были включены, вам придется
воспользоваться
Это по сути отсортировано в любом порядке? Если да, то какой заказ? Спасибо.
Ричард Данн
1
@RichardDunn Порядок будет зависеть от порядка, используемого вашей моделью по умолчанию, или от того, order_byчто было сгенерировано QuerySetвышеупомянутым filter. Я не использовал 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"% (год, месяц), "% s-% s- 1 "% (год, int (месяц) +1)]
SpiRail
195

Вы можете использовать Djangofilter с datetime.dateобъектами :

import datetime
samples = Sample.objects.filter(sampledate__gte=datetime.date(2011, 1, 1),
                                sampledate__lte=datetime.date(2011, 1, 31))
Бернхард Валлант
источник
чтобы получить все, включая день 1 и 31, мы должны будем использовать gte так?
Сэм Стоилинга
1
Преимущество использования этого метода перед кроджером заключается в том, что вы можете передавать ему объекты datetime вместо строк.
Брайан Кунг
79

При выполнении диапазонов django с фильтром убедитесь, что вы знаете разницу между использованием объекта date и объекта datetime. __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 часа в зависимости от того, какое время для полей даты установлено.

cademan
источник
5
Я думаю, что важно отметить, как импортировать dateобъект: >>> from datetime import date >>> startdate = date.today()
Алекс Спенсер
19

Вы можете обойти «несоответствие импеданса», вызванное отсутствием точности в DateTimeField/dateсравнении объектов, которое может возникнуть при использовании диапазона, с помощью datetime.timedelta, чтобы добавить день к последней дате в диапазоне. Это работает как:

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- кажется более логичным придерживаться экземпляров даты при сравнении с DateTimeField, вместо того, чтобы возиться с одноразовыми (и сбивающими с толку) datetimeобъектами. Дальнейшее объяснение в комментариях ниже.

trojjer
источник
1
Есть замечательная
библиотека Делора,
@tojjer: выглядит многообещающе, но как здесь использовать метод усечения?
Евгений
@eugene: я снова исследовал это только сейчас, после всех этих месяцев, и вы правы в том, что это действительно не помогает в этой ситуации. Единственный способ обойти это, как я могу предположить, - это предложить мой первоначальный ответ, который заключается в предоставлении дополнительного «отступа» для сравнения с полем модели даты и времени, когда вы фильтруете по экземпляру даты. Это можно сделать с помощью метода datetime.combine, как указано выше, но я обнаружил, что проще учесть расхождение, добавив временную дельту (days = 1) к дате начала / окончания диапазона - - в зависимости от проблемы.
Trojjer
Так 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
1
@tojjer: datetime_field__range = [delorean.parse ('2014-01-01'). date, delorean.parse ('2014-02-01'). date] работает для меня
Евгений
1

Просто,

YourModel.objects.filter(YOUR_DATE_FIELD__date=timezone.now())

Работает для меня

Джонатан Фахардо
источник
3
Это сработало и для меня, для новичков для ясности: (date__date = ...) означает ({whatColumnTheDateIsCalled} __ date)
Райан Дайнс
2
ОП попросил диапазон однако
aliasav
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
saran3h
источник
-2

Все еще актуально сегодня. Вы также можете сделать:

import dateutil
import pytz

date = dateutil.parser.parse('02/11/2019').replace(tzinfo=pytz.UTC)
Бхавик шах
источник