Django – Как фильтровать по дате с помощью Django Rest Framework?

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

модели.py

class Event(models.Model):
    event_type = models.CharField(
        max_length=100,
        choices=EVENT_TYPE_CHOICES,
        verbose_name=_("Event Type")
    )
    event_model = models.CharField(
        max_length=100,
        choices=EVENT_MODEL_CHOICES,
        verbose_name=_("Event Model")
    )
    timestamp = models.DateTimeField(auto_now=True, verbose_name=_("Timestamp"))

Затем я использую Django-rest-framework для создания конечной точки API для этого класса, а django-filter обеспечивает следующие функции фильтрации:

from .models import Event
from .serializers import EventSerializer
from rest_framework import viewsets, filters
from rest_framework import renderers
from rest_framework_csv import renderers as csv_renderers


class EventsView(viewsets.ReadOnlyModelViewSet):
    """
    A read only view that returns all audit events in JSON or CSV.
    """
    queryset = Event.objects.all()
    renderer_classes = (csv_renderers.CSVRenderer, renderers.JSONRenderer)
    serializer_class = EventSerializer
    filter_backends = (filters.DjangoFilterBackend,)
    filter_fields = ('event_type', 'event_model', 'timestamp')

со следующими настройками:

REST_FRAMEWORK = {
    'DEFAULT_FILTER_BACKENDS': ('rest_framework.filters.DjangoFilterBackend',),
}

Я могу фильтровать по event_type и event_model, но у меня проблемы с фильтрацией по полю метки времени. По сути, я хочу сделать вызов API, который соответствует следующему:

AuditEvent.objects.filter(timestamp__gte='2016-01-02 00:00+0000')

что, как я ожидал, я мог бы сделать следующим образом:

response = self.client.get("/api/v1/events/?timestamp=2016-01-02 00:00+0000", **{'HTTP_ACCEPT': 'application/json'})

хотя это неверно. Как сделать вызов API, который возвращает все объекты с отметкой времени, превышающей или равной определенному значению?


person orange1    schedule 12.05.2016    source источник


Ответы (5)


Чтобы расширить ответ Флейминга, если вы когда-либо собираетесь фильтровать только форматы даты и времени ISO, полезно перезаписать значения по умолчанию, чтобы всегда использовать IsoDateTimeFilter. Это можно сделать для каждого набора фильтров, например.

from django.db import models as django_models
import django_filters
from rest_framework import filters
from rest_framework import viewsets

class EventFilter(filters.FilterSet):
    class Meta:
        model = Event
        fields = {
            'timestamp': ('lte', 'gte')
        }

    filter_overrides = {
        django_models.DateTimeField: {
            'filter_class': django_filters.IsoDateTimeFilter
        },
    }

class EventsView(viewsets.ReadOnlyModelViewSet):
    ...
    filter_class = EventFilter

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

person clwainwright    schedule 10.08.2016
comment
EventFillter заставляет бэкэнд-фильтр django больше не работать - person ; 18.04.2018
comment
filter.FilterSet больше не существует в Django 2.1 - person Philipp S.; 12.09.2019
comment
Вместо этого используйте это: из django_filters импортируйте rest_framework как фильтры - person Philipp S.; 02.04.2020

Вы можете создать определенный FilterSet следующим образом:

import django_filters
from rest_framework import filters
from rest_framework import viewsets

class EventFilter(filters.FilterSet):
    timestamp_gte = django_filters.DateTimeFilter(name="timestamp", lookup_expr='gte')
    class Meta:
        model = Event
        fields = ['event_type', 'event_model', 'timestamp', 'timestamp_gte']

class EventsView(viewsets.ReadOnlyModelViewSet):
    ...
    filter_class = EventFilter

Чем вы можете фильтровать по "/api/v1/events/?timestamp_gte=2016-01-02"

EDIT: Просто чтобы уточнить, в этом примере используется django-filter библиотека.

person Flaiming    schedule 12.05.2016
comment
Хм, я все еще получаю 0 ответов, хотя должен был получить 1. Я пытался использовать как django_filters.DateTimeFilter, так и django_filters.IsoDateTimeFilter. Любая идея, почему это может быть так? - person orange1; 12.05.2016
comment
Это работает для меня, но используйте DateFilter вместо DateTimeFilter. @orange1 - person mrroot5; 17.04.2018

IsoDateTimeFilter очень требователен к формату ввода; вместо:

  • 2016-01-02 00:00+0000

использовать:

  • 2016-01-02T00:00:00Z
person Bruno Rino    schedule 26.01.2018

Лучший способ - отфильтровать дату и время в функции get_queryset.

def get_queryset(self):
    queryset = Event.objects.all()
    start_date = self.request.query_params.get('start_date', None)
    end_date = self.request.query_params.get('end_date', None)
    if start_date and end_date:
        queryset = queryset.filter(timstamp__range=[start_date, end_date])
person Cookie Zhang    schedule 29.12.2018
comment
Это не очень хороший пример, хотя он может работать для конкретного быстрого и грязного решения, но как только у вас появятся запросы разных типов, это не сработает. - person Octavio del Ser; 31.05.2019

Ни один из ответов не сработал для меня, но это сработало:

class EventFilter(filters.FilterSet):
    start = filters.IsoDateTimeFilter(field_name="start", lookup_expr='gte')
    end = filters.IsoDateTimeFilter(field_name="end", lookup_expr='lte')

    class Meta:
        model = Event
        fields = 'start', 'end',
person eggbert    schedule 06.05.2020