Creating Custom Filters in Django Rest Framework

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

Tags:- Python Django DRF

Filtering is a crucial feature for building dynamic and user-friendly APIs. While DjangoFilterBackend and django-filter handle basic filtering out of the box, sometimes you need more power. That’s where Custom Filters come in.

In this article, you’ll learn:

  • What custom filters are

  • When to use them

  • How to build them step-by-step

  • A complete working example

  • Tips and common pitfalls


What Are Custom Filters?

Custom filters allow you to go beyond simple field lookups, enabling:

  • Custom business logic in filters

  • Querying related or computed fields

  • Accepting special query parameters (e.g. ?has_discount=true)

  • Filtering with custom methods

Custom filters are created by extending django_filters.Filter and writing your own logic or using the method= parameter.


When Should You Use Custom Filters?

Use a custom filter when:

  • Built-in field lookups (like lte, icontains) aren't enough

  • You need complex conditions across multiple fields

  • You want to expose user-friendly filter parameters


Step-by-Step: Creating a Custom Filter

1. Install django-filter (if not already)

pip install django-filter

2. Define Your Models

# models.py
from django.db import models

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

3. Create a Custom FilterSet

# filters.py
import django_filters
from .models import Product

class ProductFilter(django_filters.FilterSet):
    has_discount = django_filters.BooleanFilter(method='filter_has_discount')

    def filter_has_discount(self, queryset, name, value):
        if value:
            return queryset.exclude(discount_price__isnull=True)
        return queryset

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

Explanation:

  • has_discount is a virtual filter (not a model field)

  • method='filter_has_discount' tells the filter to call that method

  • The method returns a filtered queryset based on the condition


4. Use the FilterSet in Your ViewSet

# 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

5. Test the API

Example API requests:

GET /api/products/?has_discount=true
GET /api/products/?in_stock=false&has_discount=false

✅ Advanced Custom Filter Example

Suppose you want to filter products created in the last N days.

# filters.py
from django.utils import timezone
import django_filters

class ProductFilter(django_filters.FilterSet):
    recent_days = django_filters.NumberFilter(method='filter_recent_days')

    def filter_recent_days(self, queryset, name, value):
        cutoff_date = timezone.now() - timezone.timedelta(days=value)
        return queryset.filter(created_at__gte=cutoff_date)

    class Meta:
        model = Product
        fields = ['recent_days']

Use like:

GET /api/products/?recent_days=7

✅ Full Working Example (Code Summary)

  • Model: Product with discount_price and created_at

  • FilterSet: Filters by custom logic (has_discount, recent_days)

  • ViewSet: Uses DjangoFilterBackend with custom filterset_class


Tips & Best Practices

Tip Why It Helps
✅ Use descriptive filter names (has_discount, created_after) Improves API clarity
✅ Always validate input in custom methods Prevents edge-case bugs
✅ Combine multiple custom filters in one FilterSet Keeps code clean and organized
✅ Write unit tests for your filter methods Ensures long-term maintainability
✅ Document available filters in your API docs Helps frontend/dev users

⚠️ Common Pitfalls

Pitfall Solution
❌ Filter field not working Ensure it's listed in fields or defined with method
❌ Filtering by calculated or unrelated data fails Use custom method filters and query annotation if needed
❌ Performance issues Index frequently filtered fields and profile queries
❌ Confusing parameter names Choose intuitive names like price_min, has_stock

Conclusion

Custom filters in DRF using django-filter give you the flexibility to handle real-world business logic in your APIs. With just a few lines of code, you can expose powerful filtering options that go beyond simple field lookups.

They're essential when you want your API to be expressive, performant, and easy to consume.