.. _drf integration: ==================== 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. __ http://www.django-rest-framework.org/ __ http://www.django-rest-framework.org/api-guide/filtering/ Quickstart ---------- Using the new ``FilterSet`` simply requires changing the import path. Instead of importing from ``django_filters``, import from the ``rest_framework`` sub-package. .. code-block:: python 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``. .. code-block:: python 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. .. code-block:: python # 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. .. code-block:: python 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 :ref:`Meta.fields `. .. code-block:: python 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. .. code-block:: python 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. .. code-block:: bash 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: .. image:: ../assets/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.