[Fixed]-Django – How to filter by date with Django Rest Framework?

17πŸ‘

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.

import django_filters
from django.db import models as django_models
from django_filters import rest_framework as 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.

πŸ‘€clwainwright

9πŸ‘

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(field_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.

πŸ‘€Flaiming

8πŸ‘

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

  • 2016-01-02 00:00+0000

use:

  • 2016-01-02T00:00:00Z
πŸ‘€Bruno Rino

2πŸ‘

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])
πŸ‘€Cookie Zhang

2πŸ‘

Give parameter in date format instead of timestamp

Since you want to filter by only date not timestamp, you can consider writing customized django_filters.FilterSet class.

You can do so by replacing django_filters.DateTimeFilter to django_filters.DateFilter, adding __date as suffix to your timestamp field_name and add lookup_expr as exact value.

Have a look at code below for example.

Model Class

class RegistrationLink(models.Model):
    """marketing campaign links"""
    campaign_name = models.CharField("campaign name", max_length=128, unique=True)
    url = models.URLField("URL", max_length=200, null=True, unique=True)
    created_date = models.DateTimeField("created date and time", auto_now_add=True)
    created_by = models.ForeignKey(User, verbose_name="user ID", on_delete=models.PROTECT)
    visit_count = models.PositiveIntegerField("visit count", null=False, default=0)

FilterSet Class

class RegistrationLinkFilter(django_filters.FilterSet):
    created_date = django_filters.DateFilter(field_name='created_date__date', lookup_expr="exact")
    campaign_name = django_filters.CharFilter(lookup_expr='icontains')

    class Meta:
        model = RegistrationLink
        fields = ['campaign_name', 'created_date']

class based view

import django_filters
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework.filters import OrderingFilter
# ...

class RegistrationLinkViewV2(ListAPIView):
    """Registration links for marketing campaigns"""
    permission_classes = (IsAuthenticated, ManagePermission)

    method = ''
    queryset = RegistrationLink.objects.all()
    serializer_class = LinkSerializer
    pagination_class = PageNumberPagination
    filter_backends = (DjangoFilterBackend, OrderingFilter,)
    filterset_class = RegistrationLinkFilter
    filterset_fields = ('campaign_name', 'created_date')
    ordering_fields = ('campaign_name', 'created_date',
                       'url', 'created_by__username')
    ordering = "-created_date"

    def get(self, request, *args, **kwargs):
        return self.list(request, *args, **kwargs)

1πŸ‘

I don’t know what is the case you are looking for. Basically, you can access the params from the views by
date_params = self.request.query_params.get('params_name').

Then you can do
Event.objects.filter(date__lte=date_params, date__gte=date_params)

πŸ‘€Khuong Tran

0πŸ‘

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',
πŸ‘€eggbert

Leave a comment