Querying and Filtering
DRF provides tools to efficiently query, filter, and optimize data retrieval from the database. These tools ensure that APIs deliver only the required data while maintaining performance.
Filtering and Ordering
Overview
Filtering and ordering allow you to return subsets of data or sort results based on specific fields.
Using filter_backends
- DRF provides built-in filter backends for basic filtering, search, and ordering.
- Specify
filter_backends
in the view or globally in the settings.
Example Setup
-
Enable filter backends:
from rest_framework.filters import OrderingFilter
class MyViewSet(viewsets.ModelViewSet):
queryset = MyModel.objects.all()
serializer_class = MySerializer
filter_backends = [OrderingFilter]
ordering_fields = ['field1', 'field2'] # Specify fields allowed for ordering -
Global configuration:
REST_FRAMEWORK = {
'DEFAULT_FILTER_BACKENDS': [
'rest_framework.filters.OrderingFilter',
],
}
Filtering
-
Simple Filtering: Use
django_filters
to apply simple or complex filtering based on query parameters.from django_filters.rest_framework import DjangoFilterBackend
class MyViewSet(viewsets.ModelViewSet):
queryset = MyModel.objects.all()
serializer_class = MySerializer
filter_backends = [DjangoFilterBackend]
filterset_fields = ['field1', 'field2'] # Specify fields to filter- Example URL:
/api/items/?field1=value
- Example URL:
-
Custom Filtering: Define a custom filter backend for advanced filtering logic.
from rest_framework.filters import BaseFilterBackend
class CustomFilterBackend(BaseFilterBackend):
def filter_queryset(self, request, queryset, view):
if 'custom_param' in request.query_params:
return queryset.filter(custom_field=request.query_params['custom_param'])
return queryset
class MyViewSet(viewsets.ModelViewSet):
filter_backends = [CustomFilterBackend]
Ordering
-
Add an
OrderingFilter
backend and specifyordering_fields
or allow all fields.from rest_framework.filters import OrderingFilter
class MyViewSet(viewsets.ModelViewSet):
queryset = MyModel.objects.all()
serializer_class = MySerializer
filter_backends = [OrderingFilter]
ordering_fields = ['field1', 'field2'] # Fields for ordering
ordering = ['field1'] # Default ordering- Example URL:
/api/items/?ordering=field1
- Example URL:
Pagination
Overview
Pagination splits large datasets into smaller chunks, making responses manageable and efficient.
DRF Pagination Classes
-
PageNumberPagination:
- Default pagination using page numbers.
from rest_framework.pagination import PageNumberPagination
class MyPagination(PageNumberPagination):
page_size = 10 # Number of items per page
page_size_query_param = 'page_size' # Allow clients to set page size
max_page_size = 100 # Maximum allowed page size -
LimitOffsetPagination:
- Allows clients to specify an offset and limit.
from rest_framework.pagination import LimitOffsetPagination
class MyPagination(LimitOffsetPagination):
default_limit = 10
max_limit = 100 -
CursorPagination:
- Uses an opaque cursor for pagination, ensuring stable ordering.
from rest_framework.pagination import CursorPagination
class MyPagination(CursorPagination):
page_size = 10
ordering = 'created_at' # Field used for cursor
Using Custom Pagination
Assign the pagination class to a view:
class MyViewSet(viewsets.ModelViewSet):
queryset = MyModel.objects.all()
serializer_class = MySerializer
pagination_class = MyPagination
Global Pagination
Set pagination globally in settings.py
:
REST_FRAMEWORK = {
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
'PAGE_SIZE': 10,
}
Searching
Overview
DRF provides a SearchFilter
backend for simple search functionality using query parameters.
Adding Search to a View
-
Include the
SearchFilter
backend:from rest_framework.filters import SearchFilter
class MyViewSet(viewsets.ModelViewSet):
queryset = MyModel.objects.all()
serializer_class = MySerializer
filter_backends = [SearchFilter]
search_fields = ['name', 'description'] # Fields to search -
Search query example:
- URL:
/api/items/?search=keyword
- URL:
Customizing Search
-
Partial Matching:
-
Use
^
(starts with),@
(search), or$
(regex) to control field behavior:search_fields = ['^name', '=description', '@tags']
-
-
Case-Insensitive Search:
- Apply filters like
icontains
in a custom backend.
- Apply filters like
Full-Text Search
For advanced searching, integrate with third-party libraries like Elasticsearch, Solr, or Django’s PostgreSQL full-text search.
Advanced Query Optimizations
Overview
Query optimizations reduce database queries and improve performance when fetching related objects.
Techniques
-
select_related
:- Fetches related objects in a single query for ForeignKey relationships.
queryset = MyModel.objects.select_related('related_field')
-
prefetch_related
:- Fetches related objects for ManyToMany or reverse ForeignKey relationships.
queryset = MyModel.objects.prefetch_related('related_field')
-
Using Annotate and Aggregate:
- Add computed values to a query to reduce separate calculations.
from django.db.models import Count
queryset = MyModel.objects.annotate(related_count=Count('related_field')) -
Avoid N+1 Query Problem:
- Avoid queries within loops by preloading related data.
queryset = MyModel.objects.select_related('related_field')
for obj in queryset:
print(obj.related_field.name) # Avoids additional queries -
Use QuerySet Caching:
- Cache frequently accessed data using Django’s caching framework.
-
Index Database Fields:
- Add database indexes for fields frequently used in filtering or ordering.
Example of Optimized QuerySet
queryset = MyModel.objects.select_related('foreign_key_field').prefetch_related('many_to_many_field').annotate(related_count=Count('related_field'))