CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-djangorestframework-jsonapi

A Django REST framework API adapter for the JSON:API spec.

Pending
Overview
Eval results
Files

filtering.mddocs/

Filtering

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.

Capabilities

OrderingFilter

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 ascending

DjangoFilterBackend

Enhanced 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

Sorting (Ordering)

Basic Sorting

# 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

Relationship Sorting

# 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_at

Field Name Formatting

Sorting 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_at

Validation

Invalid 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"
  }]
}

Filtering

Basic Filtering

# Equality filter
GET /articles?filter[title]=Django

# Boolean filter
GET /articles?filter[published]=true

# Numeric filter
GET /articles?filter[view_count]=100

Lookup Filters

# 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

Relationship Filtering

# 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]=News

Field Name Formatting

Filtering 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

Complex Filtering

# 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

Filter Validation

Syntax Validation

# 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"
  }]
}

Field Validation

# Invalid filter field
GET /articles?filter[invalid_field]=value

# Response:
{
  "errors": [{
    "detail": "invalid filter[invalid_field]",
    "status": "400"
  }]
}

Search Integration

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]=true

Configuration

FilterSet Configuration

import 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'],
        }

View Configuration

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']

Combined Usage

# 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 response

Types

from 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 DRFDjangoFilterBackend

Install with Tessl CLI

npx tessl i tessl/pypi-djangorestframework-jsonapi

docs

exceptions-utilities.md

filtering.md

index.md

pagination.md

relations.md

renderers-parsers.md

serializers.md

views.md

tile.json