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:
Parses the
orderingquery parameterChecks if any requested ordering fields are in
computed_ordering_fieldsFor computed fields: applies the corresponding annotation to the queryset
For standard fields: delegates to DRF’s standard ordering logic
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"),
}