A Django REST framework API adapter for the JSON:API spec.
—
JSON:API compliant filtering and sorting with proper validation, field name formatting, and integration with django-filter for advanced filtering capabilities according to JSON:API specification.
JSON:API compliant ordering filter that implements the sort parameter specification.
class OrderingFilter(rest_framework.filters.OrderingFilter):
"""
JSON:API compliant ordering filter using 'sort' parameter.
Implements https://jsonapi.org/format/#fetching-sorting
Raises 400 errors for invalid sort fields (unlike DRF's default behavior).
Supports field name formatting and relationship traversal.
"""
ordering_param = "sort" # JSON:API compliant parameter name
ordering_description = "[list of fields to sort by] (https://jsonapi.org/format/#fetching-sorting)"
def remove_invalid_fields(self, queryset, fields, view, request):
"""
Validate sort fields and raise errors for invalid fields.
Args:
queryset: QuerySet to filter
fields: List of field names to sort by
view: View instance
request: HTTP request
Returns:
list: Valid field names
Raises:
ValidationError: If any sort field is invalid
"""Usage example:
# settings.py
REST_FRAMEWORK = {
'DEFAULT_FILTER_BACKENDS': [
'rest_framework_json_api.filters.OrderingFilter',
],
}
# Or in view:
from rest_framework_json_api.filters import OrderingFilter
class ArticleViewSet(viewsets.ModelViewSet):
queryset = Article.objects.all()
filter_backends = [OrderingFilter]
ordering_fields = ['title', 'created_at', 'author__name']
ordering = ['-created_at'] # Default ordering
# Request: GET /articles?sort=title,-created_at,author.name
# Sorts by title ascending, created_at descending, author name ascendingEnhanced Django-filter backend with JSON:API compliant filter parameters and validation.
class DjangoFilterBackend(django_filters.rest_framework.DjangoFilterBackend):
"""
JSON:API compliant Django-filter backend.
Uses filter[field] parameter syntax and provides proper validation
with 400 errors for invalid filters. Supports relationship traversal
and field name formatting.
Filter syntax examples:
- filter[title]=value (equality)
- filter[title.icontains]=value (lookup)
- filter[author.name]=value (relationship)
- filter[tags.in]=tag1,tag2,tag3 (list membership)
"""
search_param = 'search' # Search parameter name
filter_regex = re.compile(r"^filter(?P<ldelim>\[?)(?P<assoc>[\w\.\-]*)(?P<rdelim>\]?$)")
def get_filterset(self, request, queryset, view):
"""
Get filterset with validation for filter parameters.
Args:
request: HTTP request
queryset: QuerySet to filter
view: View instance
Returns:
FilterSet instance or None
Raises:
ValidationError: If filter parameters are invalid
"""
def get_filterset_kwargs(self, request, queryset, view):
"""
Convert filter[field] parameters to FilterSet format.
Args:
request: HTTP request
queryset: QuerySet to filter
view: View instance
Returns:
dict: FilterSet kwargs
Raises:
ValidationError: If filter syntax is invalid
"""Usage example:
# Install django-filter
# pip install django-filter
# settings.py
INSTALLED_APPS = [
'django_filters',
# ...
]
REST_FRAMEWORK = {
'DEFAULT_FILTER_BACKENDS': [
'rest_framework_json_api.django_filters.DjangoFilterBackend',
],
}
# views.py
import django_filters
from rest_framework_json_api.django_filters import DjangoFilterBackend
class ArticleFilter(django_filters.FilterSet):
title = django_filters.CharFilter(lookup_expr='icontains')
published = django_filters.BooleanFilter()
author_name = django_filters.CharFilter(field_name='author__name', lookup_expr='icontains')
class Meta:
model = Article
fields = ['title', 'published', 'author_name']
class ArticleViewSet(viewsets.ModelViewSet):
queryset = Article.objects.all()
filterset_class = ArticleFilter
filter_backends = [DjangoFilterBackend]
# Request examples:
# GET /articles?filter[title]=Django
# GET /articles?filter[published]=true
# GET /articles?filter[author-name.icontains]=john# Single field ascending
GET /articles?sort=title
# Single field descending
GET /articles?sort=-created_at
# Multiple fields
GET /articles?sort=title,-created_at,author.name# Sort by related field
GET /articles?sort=author.name
# Sort by nested relationship
GET /articles?sort=category.parent.name
# Multiple relationship sorts
GET /articles?sort=author.name,-category.created_atSorting respects field name formatting settings:
# settings.py
JSON_API_FORMAT_FIELD_NAMES = True
# Model field: created_at
# Sort parameter: created-at
GET /articles?sort=-created-at
# Automatically converted to: created_atInvalid sort fields return proper errors:
# Invalid field
GET /articles?sort=invalid_field
# Response:
{
"errors": [{
"detail": "invalid sort parameter: invalid_field",
"status": "400"
}]
}
# Multiple invalid fields
GET /articles?sort=invalid1,invalid2
# Response:
{
"errors": [{
"detail": "invalid sort parameters: invalid1,invalid2",
"status": "400"
}]
}# Equality filter
GET /articles?filter[title]=Django
# Boolean filter
GET /articles?filter[published]=true
# Numeric filter
GET /articles?filter[view_count]=100# Case-insensitive contains
GET /articles?filter[title.icontains]=django
# Greater than
GET /articles?filter[view_count.gt]=50
# Date range
GET /articles?filter[created_at.gte]=2023-01-01
# Is null
GET /articles?filter[author.isnull]=true
# In list
GET /articles?filter[status.in]=published,draft# Filter by related field
GET /articles?filter[author.name]=John
# Filter by nested relationship
GET /articles?filter[category.parent.name]=Tech
# Multiple relationship filters
GET /articles?filter[author.email.icontains]=gmail&filter[category.name]=NewsFiltering respects field name formatting:
# settings.py
JSON_API_FORMAT_FIELD_NAMES = True
# Model field: created_at
# Filter parameter: created-at
GET /articles?filter[created-at.gte]=2023-01-01
# Model field: author_name
# Filter parameter: author-name
GET /articles?filter[author-name.icontains]=john# Multiple filters (AND logic)
GET /articles?filter[published]=true&filter[author.name]=John&filter[view_count.gt]=100
# List membership
GET /articles?filter[tags.name.in]=python,django,api
# Date range filtering
GET /articles?filter[created_at.gte]=2023-01-01&filter[created_at.lt]=2024-01-01# Invalid filter syntax
GET /articles?filter=invalid
# Response:
{
"errors": [{
"detail": "invalid query parameter: filter",
"status": "400"
}]
}
# Missing value
GET /articles?filter[title]=
# Response:
{
"errors": [{
"detail": "missing value for query parameter filter[title]",
"status": "400"
}]
}# Invalid filter field
GET /articles?filter[invalid_field]=value
# Response:
{
"errors": [{
"detail": "invalid filter[invalid_field]",
"status": "400"
}]
}The DjangoFilterBackend integrates with SearchFilter:
from rest_framework.filters import SearchFilter
from rest_framework_json_api.django_filters import DjangoFilterBackend
class ArticleViewSet(viewsets.ModelViewSet):
queryset = Article.objects.all()
filter_backends = [DjangoFilterBackend, SearchFilter]
filterset_fields = ['published', 'author']
search_fields = ['title', 'content']
# Use filter[search] to avoid conflicts
GET /articles?filter[search]=django&filter[published]=trueimport django_filters
class ArticleFilter(django_filters.FilterSet):
# Custom filter methods
title_search = django_filters.CharFilter(method='filter_title_search')
date_range = django_filters.DateFromToRangeFilter(field_name='created_at')
def filter_title_search(self, queryset, name, value):
return queryset.filter(
Q(title__icontains=value) | Q(content__icontains=value)
)
class Meta:
model = Article
fields = {
'title': ['exact', 'icontains'],
'published': ['exact'],
'created_at': ['gte', 'lte'],
'author': ['exact'],
'author__name': ['exact', 'icontains'],
}class ArticleViewSet(viewsets.ModelViewSet):
queryset = Article.objects.all()
filterset_class = ArticleFilter
filter_backends = [
DjangoFilterBackend,
OrderingFilter,
]
ordering_fields = ['title', 'created_at', 'author__name']
ordering = ['-created_at']# Complex query with filtering, sorting, pagination, and includes
GET /articles?filter[published]=true&filter[author.name.icontains]=john&sort=-created_at,title&page[number]=2&page[size]=10&include=author,tags
# This request:
# 1. Filters published articles by authors containing "john"
# 2. Sorts by created_at descending, then title ascending
# 3. Returns page 2 with 10 items per page
# 4. Includes author and tags in the responsefrom rest_framework_json_api.filters import OrderingFilter
from rest_framework_json_api.django_filters import DjangoFilterBackend
# Base classes
from rest_framework.filters import OrderingFilter as DRFOrderingFilter
from django_filters.rest_framework import DjangoFilterBackend as DRFDjangoFilterBackendInstall with Tessl CLI
npx tessl i tessl/pypi-djangorestframework-jsonapi