Integration with DRF

Integration with Django Rest Framework is provided through a DRF-specific FilterSet and a filter backend. These may be found in the rest_framework sub-package.

Quickstart

Using the new FilterSet simply requires changing the import path. Instead of importing from django_filters, import from the rest_framework sub-package.

from django_filters import rest_framework as filters

class ProductFilter(filters.FilterSet):
    ...

Your view class will also need to add DjangoFilterBackend to the filter_backends.

from django_filters import rest_framework as filters

class ProductList(generics.ListAPIView):
    queryset = Product.objects.all()
    serializer_class = ProductSerializer
    filter_backends = (filters.DjangoFilterBackend,)
    filterset_fields = ('category', 'in_stock')

If you want to use the django-filter backend by default, add it to the DEFAULT_FILTER_BACKENDS setting.

# settings.py
INSTALLED_APPS = [
    # ...
    'rest_framework',
    'django_filters',
]

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

Adding a FilterSet with filterset_class

To enable filtering with a FilterSet, add it to the filterset_class parameter on your view class.

from rest_framework import generics
from django_filters import rest_framework as filters
from myapp import Product


class ProductFilter(filters.FilterSet):
    min_price = filters.NumberFilter(field_name="price", lookup_expr='gte')
    max_price = filters.NumberFilter(field_name="price", lookup_expr='lte')

    class Meta:
        model = Product
        fields = ['category', 'in_stock']


class ProductList(generics.ListAPIView):
    queryset = Product.objects.all()
    serializer_class = ProductSerializer
    filter_backends = (filters.DjangoFilterBackend,)
    filterset_class = ProductFilter

Using the filterset_fields shortcut

You may bypass creating a FilterSet by instead adding filterset_fields to your view class. This is equivalent to creating a FilterSet with just Meta.fields.

from rest_framework import generics
from django_filters import rest_framework as filters
from myapp import Product


class ProductList(generics.ListAPIView):
    queryset = Product.objects.all()
    filter_backends = (filters.DjangoFilterBackend,)
    filterset_fields = ('category', 'in_stock')


# Equivalent FilterSet:
class ProductFilter(filters.FilterSet):
    class Meta:
        model = Product
        fields = ('category', 'in_stock')

Note that using filterset_fields and filterset_class together is not supported.

Overriding FilterSet creation

FilterSet creation can be customized by overriding the following methods on the backend class:

  • .get_filterset(self, request, queryset, view)

  • .get_filterset_class(self, view, queryset=None)

  • .get_filterset_kwargs(self, request, queryset, view)

You can override these methods on a case-by-case basis for each view, creating unique backends, or these methods can be used to write your own hooks to the view class.

class MyFilterBackend(filters.DjangoFilterBackend):
    def get_filterset_kwargs(self, request, queryset, view):
        kwargs = super().get_filterset_kwargs(request, queryset, view)

        # merge filterset kwargs provided by view class
        if hasattr(view, 'get_filterset_kwargs'):
            kwargs.update(view.get_filterset_kwargs())

        return kwargs


class BookFilter(filters.FilterSet):
    def __init__(self, *args, author=None, **kwargs):
        super().__init__(*args, **kwargs)
        # do something w/ author


class BookViewSet(viewsets.ModelViewSet):
    filter_backends = [MyFilterBackend]
    filterset_class = BookFilter

    def get_filterset_kwargs(self):
        return {
            'author': self.get_author(),
        }

Crispy Forms

If you are using DRF’s browsable API or admin API you may also want to install django-crispy-forms, which will enhance the presentation of the filter forms in HTML views, by allowing them to render Bootstrap 3 HTML. Note that this isn’t actively supported, although pull requests for bug fixes are welcome.

pip install django-crispy-forms

With crispy forms installed and added to Django’s INSTALLED_APPS, the browsable API will present a filtering control for DjangoFilterBackend, like so:

../_images/form.png

Additional FilterSet Features

The following features are specific to the rest framework FilterSet:

  • BooleanFilter’s use the API-friendly BooleanWidget, which accepts lowercase true/false.

  • Filter generation uses IsoDateTimeFilter for datetime model fields.

  • Raised ValidationError’s are reraised as their DRF equivalent.