Filters

drf-commons provides filter backends that extend DRF’s built-in filtering with support for computed and annotated fields.

ComputedOrderingFilter

from drf_commons.filters import ComputedOrderingFilter

Extends DRF’s OrderingFilter to support ordering on computed fields — fields derived from annotations, aggregations, or conditional expressions that do not exist as columns in the database table.

Problem:

Standard OrderingFilter can only order by actual model fields. Annotated fields (e.g., Count('comments'), Sum('line_items__amount')) must be added to the queryset before ordering can be applied. The standard filter backend has no mechanism for this.

Solution:

ComputedOrderingFilter reads a computed_ordering_fields dict from the ViewSet. When the requested ordering field is a key in this dict, the filter applies the annotation to the queryset before ordering.

ViewSet configuration:

from django.db.models import Count, Sum
from drf_commons.filters import ComputedOrderingFilter
from drf_commons.views import BaseViewSet

class ArticleViewSet(BaseViewSet):
    filter_backends = [ComputedOrderingFilter]
    ordering_fields = ["title", "created_at", "updated_at"]

    # Computed fields: key is query param value, value is annotation
    computed_ordering_fields = {
        "comment_count": Count("comments"),
        "total_likes": Count("likes"),
    }

Query parameter usage:

GET /articles/?ordering=comment_count      — ascending by comment count
GET /articles/?ordering=-comment_count     — descending by comment count
GET /articles/?ordering=title              — standard field ordering

Behavior:

  1. Parses the ordering query parameter

  2. Checks if any requested ordering fields are in computed_ordering_fields

  3. For computed fields: applies the corresponding annotation to the queryset

  4. For standard fields: delegates to DRF’s standard ordering logic

  5. Applies combined ordering to the annotated queryset

Complex annotations:

from django.db.models import Case, DecimalField, Sum, When

class OrderViewSet(BaseViewSet):
    computed_ordering_fields = {
        # Order by computed revenue (quantity * unit_price)
        "revenue": Sum(
            Case(
                When(status="completed", then=models.F("total")),
                default=0,
                output_field=DecimalField(),
            )
        ),
    }

Combining with DRF Filter Backends

ComputedOrderingFilter is compatible with DRF’s standard filter backends:

from django_filters.rest_framework import DjangoFilterBackend
from rest_framework.filters import SearchFilter
from drf_commons.filters import ComputedOrderingFilter

class ArticleViewSet(BaseViewSet):
    filter_backends = [DjangoFilterBackend, SearchFilter, ComputedOrderingFilter]
    filterset_class = ArticleFilterSet
    search_fields = ["title", "content"]
    ordering_fields = ["title", "created_at"]
    computed_ordering_fields = {
        "comment_count": Count("comments"),
    }