Mastering DjangoFilterBackend in Django Rest Framework

Last updated 1 month, 1 week ago | 139 views 75     5

Tags:- Python Django DRF

Filtering is one of the most common and essential features in any API. Whether you're narrowing down a list of blog posts by author or fetching products under a certain price, filtering makes your APIs powerful and flexible.

Django Rest Framework (DRF) provides excellent filtering support via DjangoFilterBackend, which is built on top of the popular django-filter package.


What is DjangoFilterBackend?

DjangoFilterBackend is a filter backend in DRF that integrates the django-filter library, allowing you to filter querysets using URL parameters. It supports:

  • Simple field-based filters

  • Complex custom filters

  • Lookups like lt, gte, in, etc.


Installation and Setup

Step 1: Install django-filter

pip install django-filter

Step 2: Add to INSTALLED_APPS

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

Step 3: Add to DRF Configuration (Optional)

# settings.py
REST_FRAMEWORK = {
    'DEFAULT_FILTER_BACKENDS': ['django_filters.rest_framework.DjangoFilterBackend']
}

Basic Usage

You can enable filtering by adding DjangoFilterBackend to your view and setting the filterset_fields.

Example ViewSet:

from rest_framework import viewsets
from django_filters.rest_framework import DjangoFilterBackend
from .models import Product
from .serializers import ProductSerializer

class ProductViewSet(viewsets.ModelViewSet):
    queryset = Product.objects.all()
    serializer_class = ProductSerializer
    filter_backends = [DjangoFilterBackend]
    filterset_fields = ['category', 'in_stock', 'price']

Example Request:

GET /api/products/?category=electronics&in_stock=true&price__lt=500

This filters products by category, availability, and price.


Using Custom FilterSet Class

If you want more control (like filtering by ranges, custom logic, or using multiple field lookups), define a FilterSet.

filters.py:

import django_filters
from .models import Product

class ProductFilter(django_filters.FilterSet):
    price_min = django_filters.NumberFilter(field_name="price", lookup_expr='gte')
    price_max = django_filters.NumberFilter(field_name="price", lookup_expr='lte')
    name = django_filters.CharFilter(lookup_expr='icontains')

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

views.py:

from .filters import ProductFilter

class ProductViewSet(viewsets.ModelViewSet):
    queryset = Product.objects.all()
    serializer_class = ProductSerializer
    filter_backends = [DjangoFilterBackend]
    filterset_class = ProductFilter

Example Request:

GET /api/products/?price_min=100&price_max=1000&name=phone

✅ Full Example

models.py

from django.db import models

class Product(models.Model):
    name = models.CharField(max_length=100)
    category = models.CharField(max_length=50)
    price = models.DecimalField(max_digits=10, decimal_places=2)
    in_stock = models.BooleanField(default=True)
    created_at = models.DateTimeField(auto_now_add=True)

serializers.py

from rest_framework import serializers
from .models import Product

class ProductSerializer(serializers.ModelSerializer):
    class Meta:
        model = Product
        fields = '__all__'

filters.py

import django_filters
from .models import Product

class ProductFilter(django_filters.FilterSet):
    price_min = django_filters.NumberFilter(field_name="price", lookup_expr='gte')
    price_max = django_filters.NumberFilter(field_name="price", lookup_expr='lte')

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

views.py

from rest_framework import viewsets
from django_filters.rest_framework import DjangoFilterBackend
from .models import Product
from .serializers import ProductSerializer
from .filters import ProductFilter

class ProductViewSet(viewsets.ModelViewSet):
    queryset = Product.objects.all()
    serializer_class = ProductSerializer
    filter_backends = [DjangoFilterBackend]
    filterset_class = ProductFilter

Tips and Best Practices

Tip Reason
✅ Use filterset_class for complex filtering Cleaner code and more control
✅ Name custom filters clearly (e.g., price_min) Improves API usability
✅ Index frequently filtered fields in the DB Improves performance
✅ Document available filters Helps API consumers
✅ Combine with pagination and ordering For robust API endpoints

⚠️ Common Pitfalls

Pitfall Solution
❌ Filters not working Ensure DjangoFilterBackend is in filter_backends
❌ Error on unknown filter field DRF throws a 400; whitelist fields using filterset_fields or Meta.fields
❌ Poor performance on large datasets Add DB indexes, optimize queries
❌ Typos in query params Use clear, predictable field names

Combine with Other Backends

You can combine DjangoFilterBackend with other backends like:

from rest_framework.filters import SearchFilter, OrderingFilter

filter_backends = [
    DjangoFilterBackend,
    SearchFilter,
    OrderingFilter
]

Conclusion

DjangoFilterBackend is one of the most useful tools in Django Rest Framework for building dynamic and customizable APIs. Whether you want simple filters or advanced custom logic, django-filter has you covered.

With clean separation between views and filter logic, it helps you keep code organized while delivering robust filtering capabilities to your API consumers.