Django - How to filter by date with Django Rest Framework?

依然范特西╮ 提交于 2021-02-17 21:35:29

问题


I have some model with a timestamp field:

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

I'm then using Django-rest-framework to create an API endpoint for this class, with django-filter providing a filtering functionality as follows:

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

with the following settings:

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

I'm able to filter by event_type and event_model, but am having trouble filtering by the timestamp field. Essentially, I want to make an API call that equates to the following:

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

which I would expect I could do as follows:

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

though that is incorect. How do I make an API call that returns all objects with a timestamp greater than or equal to a certain value?


回答1:


To expand on Flaiming's answer, if you're only ever going to be filtering via ISO datetime formats, it helps to overwrite the defaults to always use the IsoDateTimeFilter. This can be done per filterset with e.g.

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

You then won't have to worry about setting a different filter for each lookup expression and each field.




回答2:


You can create specific FilterSet as follows:

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

Than you can filter by "/api/v1/events/?timestamp_gte=2016-01-02"

EDIT: Just to clarify, this example uses django-filter library.




回答3:


IsoDateTimeFilter is very picky about the input format; instead of:

  • 2016-01-02 00:00+0000

use:

  • 2016-01-02T00:00:00Z



回答4:


A better way is to filter datetime in get_queryset function

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



回答5:


None of the answers worked for me but this did:

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',


来源:https://stackoverflow.com/questions/37183943/django-how-to-filter-by-date-with-django-rest-framework

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!