DRF Filtering, Ordering & Pagination: Building Fast Search APIs

An API endpoint returning 100,000 unpaginated records will crash your database and overwhelm client devices with massive JSON payloads. DRF provides robust tools to paginate, filter, and sort results efficiently.

Configuring Advanced Filtering with django-filter

# Install django-filter
pip install django-filter

Define a dedicated FilterSet with custom lookups:

# blog/filters.py
import django_filters
from .models import BlogPost

class BlogPostFilter(django_filters.FilterSet):
    min_price = django_filters.NumberFilter(field_name="premium_price", lookup_expr='gte')
    max_price = django_filters.NumberFilter(field_name="premium_price", lookup_expr='lte')
    category_slug = django_filters.CharFilter(field_name="category__slug", lookup_expr='iexact')

    class Meta:
        model = BlogPost
        fields = ['category_slug', 'is_published', 'min_price', 'max_price']

Attaching Filters and Pagination to ModelViewSet

# blog/views.py
from rest_framework import viewsets, filters
from django_filters.rest_framework import DjangoFilterBackend
from .models import BlogPost
from .serializers import BlogPostSerializer
from .filters import BlogPostFilter

class BlogPostViewSet(viewsets.ModelViewSet):
    queryset = BlogPost.objects.filter(is_published=True)
    serializer_class = BlogPostSerializer
    
    # 3 Filter Backends configured simultaneously
    filter_backends = [
        DjangoFilterBackend,   # Exact & range filtering
        filters.SearchFilter,         # Full-text search (?search=...)
        filters.OrderingFilter       # Dynamic sorting (?ordering=-created_at)
    ]
    
    filterset_class = BlogPostFilter
    search_fields = ['title', 'summary', 'author__user__username']
    ordering_fields = ['created_at', 'premium_price', 'view_count']
    ordering = ['-created_at']  # Default sort

Clients can now perform powerful, compound queries effortlessly:

GET /api/posts/?category_slug=tech&min_price=10&search=django&ordering=-view_count&page=2

Building a Custom PageNumberPagination Class

Customize page sizes and client overrides by subclassing PageNumberPagination:

# blog/pagination.py
from rest_framework.pagination import PageNumberPagination

class CustomArticlePagination(PageNumberPagination):
    page_size = 12                      # Default items per page
    page_size_query_param = 'page_size' # Allows client: ?page_size=24
    max_page_size = 100                 # Hard cap to protect server memory
    page_query_param = 'p'              # Parameter: ?p=2

DRF Pagination Strategies Compared

Pagination Type URL Query Format Ideal Real-World Use Case
PageNumberPagination ?page=3&page_size=10 Standard web tables, admin lists, blog article directories.
LimitOffsetPagination ?limit=20&offset=40 Mobile apps, flexible client-side infinite scroll libraries.
CursorPagination ?cursor=cD0yMDI2LTEx High-velocity real-time social feeds, live notification feeds.

Frequently Asked Questions (FAQ) - Student & Interview Review

Q1. What types of pagination styles does DRF support?

DRF supports: 1) `PageNumberPagination` (e.g. `?page=2&page_size=20`), 2) `LimitOffsetPagination` (e.g. `?limit=20&offset=40`), and 3) `CursorPagination` (opaque encoded tokens that prevent missing or duplicate records when database rows are inserted rapidly in real-time).

Q2. How does SearchFilter work in DRF?

`SearchFilter` inspects the `?search=term` query parameter and performs case-insensitive containment searches across all fields defined in the view's `search_fields` list.

Q3. What package is needed for advanced field-based filtering in DRF?

Install `django-filter` (`pip install django-filter`) and add `'django_filters'` to `INSTALLED_APPS`. Then specify `filter_backends = [DjangoFilterBackend]` in your views.

Q4. Why is CursorPagination better for high-velocity real-time feeds?

Page-number pagination is vulnerable to the 'page shift' problem — if a new item is inserted on page 1 while a user is reading, the 10th item is pushed onto page 2, causing the user to see that record twice when navigating to page 2. Cursor pagination uses index cursors to eliminate this issue.

Explore Related Django Tutorials & Next Steps

Broaden your backend architecture, REST API design, and production engineering expertise:

DRF ViewSets & Routers

Apply filterset_fields and search_fields to ViewSets.

Django ORM QuerySets & Filtering

Understand the underlying ORM queries executed by filter backends.

ORM Performance & N+1 Solved

Optimize paginated queries with select_related to keep APIs fast.

15+ Django Practice Coding Exercises

Build a filterable product catalog API with price ranges and search.

Share Share on Facebook Share on Twitter Share on LinkedIn Pin on Pinterest Share on Reddit

You may also like this!