Як я фільтрую об’єкти запитів за діапазоном дат у Django?


248

У мене є поле в одній моделі, як:

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

Тепер мені потрібно фільтрувати об’єкти за діапазоном дат.

Як я фільтрую всі об'єкти, які мають дату між 1-Jan-2011і 31-Jan-2011?

Відповіді:


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 (більше-ніж / менше-ніж).


Який тип даних date1? Зараз у мене є об’єкт datetime.
user469652

8
@dcordjer: Додатково слід сказати, що __rangeвключає межі (як, наприклад, sql BETWEEN), якщо ви не хочете, щоб межі були включені, вам доведеться перейти з моїм рішенням gt / lt ...
Bernhard Vallant,

Це впорядковано в будь-якому порядку? Якщо так, то який наказ? Дякую.
Річард Данн

1
@RichardDunn Замовлення буде ґрунтуватися на замовленнях за замовчуванням вашої моделі, або якщо ви використовуєте order_byнад створеним QuerySetвищезгаданим filter. Я не користувався Джанго роками.
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

Ви можете використовувати джангоfilter з 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
Перевагою використання цього методу над crodjer є те, що ви можете передавати йому об'єкти datetime замість рядків.
Брайан Кунг

79

Виконуючи діапазони джанго з фільтром, переконайтеся, що ви знаєте різницю між використанням об’єкта дати та об'єктом дати. __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()
Алекс Спенсер

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об'єктами. Подальше пояснення див. У коментарях нижче.


1
Існує дивовижна бібліотека Delorean, яка спрощує це методом усічення: delorean.readthedocs.org/en/latest/quickstart.html#truncation
trojjer

@tojjer: виглядає багатообіцяючим, як же ми тут використовуємо метод усікання?
Євген

@eugene: Я вивчив це ще раз, після всіх тих місяців, і ти маєш рацію, що він насправді не допомагає в цій ситуації. Єдиний спосіб, який я можу придумати, - це, як було запропоновано в моїй оригінальній відповіді, - це подання додаткового «набивання» для порівняння з полем моделі моделі datetime, коли ви фільтруєте проти екземпляра дати. Це можна зробити за допомогою методу datetime.combine, як описано вище, але я виявив, що просто зрушити розбіжність може бути простішим шляхом додавання тиммеделі (днів = 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)також не працює, що, безумовно, контр-інтуїтивно зрозумілий IMHO.
trojjer

1
@tojjer: datetime_field__range = [delorean.parse ('2014-01-01'). дата, delorean.parse ('2014-02-01'). дата] працює для мене
eugene

1

Це просто,

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

Працює для мене


3
Це працювало і для мене, для нообів для ясності: (date__date = ...) означає ({whatColumnTheDateIsCalled} __ дата)
Ryan Dines

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

-2

Дотепер актуальна і сьогодні. Ви також можете зробити:

import dateutil
import pytz

date = dateutil.parser.parse('02/11/2019').replace(tzinfo=pytz.UTC)
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.