Filtering
DRF's generic list views will return the entire queryset for a model manager. Often you will want your API to restrict the items returned by the queryset, which is achieved by filtering.
Default Behavior
By default, DRF's DEFAULT_FILTER_BACKENDS setting is an empty list []. This means no advanced filtering behavior is applied out of the box unless explicitly configured.
ViewSets & Generic Views
All filtering mechanisms discussed on this page apply equally to both GenericAPIView subclasses (like ListAPIView) and GenericViewSet descendants (like ModelViewSet or ReadOnlyModelViewSet). Be aware that a plain ViewSet subclasses APIView and completely ignores filter_backends without raising an error.
Basic Filtering
The simplest way to filter the queryset of any view that subclasses GenericAPIView is to override the .get_queryset() method.
Filtering against the current user
You might want to filter the queryset to ensure that only results relevant to the currently authenticated user making the request are returned.
from rest_framework import generics
from .models import Purchase
from .serializers import PurchaseSerializer
class PurchaseList(generics.ListAPIView):
serializer_class = PurchaseSerializer
def get_queryset(self):
"""
Restricts the returned purchases to the currently authenticated user.
"""
user = self.request.user
return Purchase.objects.filter(purchaser=user)Filtering against the URL
Another common style of filtering is to restrict the queryset based on some part of the URL. For example, if your URL conf included a parameter such as re_path('^purchases/(?P<username>.+)/$', PurchaseList.as_view()), you could filter based on the username.
class PurchaseList(generics.ListAPIView):
serializer_class = PurchaseSerializer
def get_queryset(self):
"""
Restricts the returned purchases to a given user,
by filtering against a `username` parameter in the URL.
"""
username = self.kwargs['username']
return Purchase.objects.filter(purchaser__username=username)Filtering against query parameters
A final example of filtering the initial queryset is to determine the initial queryset based on query parameters in the URL.
class PurchaseList(generics.ListAPIView):
serializer_class = PurchaseSerializer
def get_queryset(self):
"""
Optionally restricts the returned purchases to a given status,
by filtering against a `status` query parameter in the URL.
"""
queryset = Purchase.objects.all()
status = self.request.query_params.get('status')
if status is not None:
queryset = queryset.filter(status=status)
return querysetFilter Backends
As well as being able to override the default queryset, DRF also supports Filter Backends - pluggable classes that filter the queryset in generic views and ViewSets. This keeps your code DRY by allowing you to define filtering logic once and reuse it across multiple views, rather than duplicating .get_queryset() logic everywhere.
The built-in SearchFilter and OrderingFilter power search boxes and table sorting from URL query parameters and the django-filter integration provides highly customizable field filtering.
You can enable filter backends globally by using the DEFAULT_FILTER_BACKENDS setting, or on a per-view basis using the filter_backends attribute, which overrides the global setting.
REST_FRAMEWORK = {
'DEFAULT_FILTER_BACKENDS': [
'rest_framework.filters.SearchFilter',
'rest_framework.filters.OrderingFilter',
'django_filters.rest_framework.DjangoFilterBackend', # requires installing the third-party `django-filter` package
]
}SearchFilter
The SearchFilter class supports simple single query parameter based searching and is based on the Django admin's search functionality.
from django.contrib.auth.models import User
from rest_framework.filters import SearchFilter
from rest_framework import generics
from .serializers import UserSerializer
class UserListView(generics.ListAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer
filter_backends = [SearchFilter]
search_fields = ['username', 'email', 'profile__bio']Clients can then filter the list with: http://example.com/api/users?search=russell
NOTE
The SearchFilter class will only be applied if the view has a search_fields attribute set. This attribute should be a list of names of text type fields on the model, such as CharField or TextField.
Search Behavior
When the search parameter contains multiple terms (separated by whitespace or commas), objects are returned only if all provided terms match across the specified fields (an AND condition between terms). For example, ?search=russell smith requires both "russell" and "smith" to match somewhere in the fields.
However, within a single term, the search evaluates all search_fields using a SQL OR condition. If you want to search for a phrase containing spaces, you can wrap it in quotes. Quoted phrases (like ?search="russell smith") are treated as a single term.
You can control the matching behavior by prepending a prefix to the field name.
For example, ['=username', '^email'] forces an exact username match or a starts-with email match.
| Prefix | Lookup | Description |
|---|---|---|
None | icontains | Contains search (Default). |
^ | istartswith | Starts-with search. |
= | iexact | Exact matches. |
$ | iregex | Regex search (see warning below). |
@ | search | Full-text search (Currently only supported in Django's PostgreSQL backend). |
Regex Search Risks
When passing a regex to the filter via the $ prefix, beware of maliciously crafted regular expressions that may lead to excessive CPU consumption and denial of service (DoS). Consider avoiding regex search for untrusted clients.
TIP
The default query parameter is search. You can globally override this by setting SEARCH_PARAM in your REST_FRAMEWORK settings.
Foreign Keys
You can span relationships to search against related models using Django's double-underscore syntax (e.g., profile__bio).
NOTE
This double-underscore syntax for spanning relationships also applies to the OrderingFilter and Generic Filtering discussed below. You can also use this same syntax to perform nested lookups into JSONField or HStoreField data (e.g., search_fields = ['data__breed']).
Accent-insensitive Search
To perform accent-insensitive searches (where querying Jeremy matches Jérémy), use UnaccentedSearchFilter instead of SearchFilter.
This class works identically, but wraps lookups with the unaccent transform (e.g., ^ becomes unaccent__istartswith). Only the @ (full-text search) prefix is left unchanged, as the unaccent transform cannot be combined with a full-text search lookup.
NOTE
This feature only supports Django's PostgreSQL backend and requires both the PostgreSQL unaccent extension and django.contrib.postgres in your INSTALLED_APPS.
OrderingFilter
The OrderingFilter class allows clients to sort the result set by sending a specific query parameter (by default ordering), passing the name of the field to sort by.
from django.contrib.auth.models import User
from rest_framework.filters import OrderingFilter
from rest_framework import generics
from .serializers import UserSerializer
class UserListView(generics.ListAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer
filter_backends = [OrderingFilter]
ordering_fields = ['username', 'email']Clients can then order the list by appending the field name: http://example.com/api/users?ordering=username
NOTE
If you don't specify an ordering_fields attribute on the view, the filter class will default to allowing the user to order on any readable fields on the serializer specified by the serializer_class attribute. You can explicitly allow ordering on all model fields by setting ordering_fields = '__all__'. However, explicitly listing fields is strongly recommended to prevent unexpected data leakage (such as letting users order against a password hash or other sensitive data).
Ordering Behavior
You can control the sorting behavior using the following patterns:
- Ascending: Pass the field name directly (e.g.,
?ordering=username). - Descending: Prefix the field name with a
-(e.g.,?ordering=-username). - Multiple Orderings: Separate multiple fields with a comma (e.g.,
?ordering=account,username).
TIP
The default query parameter is ordering. You can change this globally by setting ORDERING_PARAM in your REST_FRAMEWORK settings.
Default Ordering
If you want to specify a default ordering when the client does not provide an ordering parameter, you can set the ordering attribute on your view. The ordering attribute may be either a string or a list/tuple of strings:
class UserListView(generics.ListAPIView):
# ...
ordering = ['username']NOTE
You can also control this by setting order_by on the initial queryset directly, but setting ordering on the view ensures the ordering context is automatically passed to rendered templates (which is useful for rendering column headers in the browsable API).
Generic Filtering
While DRF includes SearchFilter and OrderingFilter, it also provides a DjangoFilterBackend class which supports highly customizable field filtering using the third-party django-filter package.
Setup
First, install django-filter:
pip install django-filterThen add 'django_filters' to your INSTALLED_APPS and configure the backend in your settings.py:
REST_FRAMEWORK = {
'DEFAULT_FILTER_BACKENDS': ['django_filters.rest_framework.DjangoFilterBackend']
}Filter Behavior
You can configure fields on a per-view basis using the filterset_fields attribute:
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework import generics
from .models import Product
from .serializers import ProductSerializer
class ProductList(generics.ListAPIView):
queryset = Product.objects.all()
serializer_class = ProductSerializer
# filter_backends = [DjangoFilterBackend] # Optional if set globally
filterset_fields = ['category', 'in_stock', 'manufacturer__name']This creates an endpoint that can be filtered using: http://example.com/api/products?category=clothing&in_stock=True&manufacturer__name=nike
NOTE
By default, the fields listed in filterset_fields map exactly to the query parameters by name using an exact match lookup. If you wish to change the query parameter name or behavior, see Custom FilterSets below.
Custom FilterSets
The filterset_fields shortcut also accepts a dictionary, allowing you to specify different lookup types (such as icontains instead of an exact match):
filterset_fields = {'price': ['gte', 'lte'], 'category': ['exact']}
# This creates filters like ?price__gte=10&price__lte=50However, if you need even more advanced behavior - like changing the query parameter name (e.g., min_price instead of price__gte), adding computed filters, or custom method filters - you must define a custom FilterSet class.
Here is an example of creating a custom filter that allows filtering by a price range and performing a case-insensitive search on a manufacturer's name:
import django_filters
from .models import Product
class ProductFilter(django_filters.FilterSet):
min_price = django_filters.NumberFilter(field_name="price", lookup_expr='gte')
max_price = django_filters.NumberFilter(field_name="price", lookup_expr='lte')
manufacturer = django_filters.CharFilter(field_name="manufacturer__name", lookup_expr='icontains')
class Meta:
model = Product
fields = ['category', 'in_stock']Once defined, apply it to your view using the filterset_class attribute instead of filterset_fields:
from rest_framework import generics
from .models import Product
from .serializers import ProductSerializer
from .filters import ProductFilter
class ProductList(generics.ListAPIView):
queryset = Product.objects.all()
serializer_class = ProductSerializer
filterset_class = ProductFilterFor more complex configurations, refer to the django-filter documentation.
Custom Filter Backends
To implement a custom generic filter backend, subclass BaseFilterBackend and override the .filter_queryset(self, request, queryset, view) method. The method should return a new, filtered queryset.
As well as allowing clients to send custom search parameters, custom filter backends can be useful for defining logic to restrict the overall queryset that should be visible to any given request.
Example: Filtering by Owner
A common use case is restricting users to only see objects they created. Be sure to pair this with a permission class like IsAuthenticated, or guard against anonymous users inside the filter to prevent a TypeError.
from rest_framework import filters
class IsOwnerFilterBackend(filters.BaseFilterBackend):
"""
Filter that only allows users to see their own objects.
"""
def filter_queryset(self, request, queryset, view):
if not request.user.is_authenticated:
return queryset.none()
return queryset.filter(owner=request.user)You can then apply this filter to a view or ViewSet:
from rest_framework.viewsets import ModelViewSet
from .models import Order
from .serializers import OrderSerializer
from .filters import IsOwnerFilterBackend
class OrderViewSet(ModelViewSet):
queryset = Order.objects.all()
serializer_class = OrderSerializer
filter_backends = [IsOwnerFilterBackend]Browsable API Integration
If you want your custom filter to present controls in the browsable API, you should also implement a .to_html(self, request, queryset, view) method that returns a rendered HTML string.
Note that built-in backends like SearchFilter and OrderingFilter automatically render filter controls in the browsable API when configured.
Advanced Filtering Behavior
Object Lookups
Note that if a filter backend is configured for a view, then as well as being used to filter list views (collection API), it will also be used to filter the querysets used for returning a single object (detail API).
For example, if a user requests a detail view but includes filter parameters in the URL (e.g., /api/products/4675/?category=clothing&max_price=10.00) and the specific product does not match those filters, they will receive a 404 Not Found instead of the object itself.
Overriding the Initial Queryset
Note that you can use both an overridden .get_queryset() and generic filtering together and everything will work as expected. For example, if Product had a many-to-many relationship with User, named purchase, you might want to write a view like this:
from rest_framework import generics
from .serializers import ProductSerializer
from .filters import ProductFilter
class PurchasedProductsList(generics.ListAPIView):
"""
Return a list of all the products that the authenticated
user has ever purchased, with optional filtering.
"""
serializer_class = ProductSerializer
filterset_class = ProductFilter
def get_queryset(self):
user = self.request.user
return user.purchase_set.all()TIP
You can find more third-party packages for filtering at the official DRF documentation.
