authentik.api.ordering
1from django.db.models import F, QuerySet 2from rest_framework.filters import OrderingFilter 3from rest_framework.request import Request 4from rest_framework.views import APIView 5 6 7class NullsAwareOrderingFilter(OrderingFilter): 8 """OrderingFilter that sorts NULL values consistently. 9 10 For any nullable field, NULLs are treated as the smallest possible value: 11 - ascending → NULLs appear first (nulls_first=True) 12 - descending → NULLs appear last (nulls_last=True) 13 """ 14 15 def _nullable_field_names(self, queryset: QuerySet) -> set[str]: 16 return {f.name for f in queryset.model._meta.get_fields() if hasattr(f, "null") and f.null} 17 18 def filter_queryset(self, request: Request, queryset: QuerySet, view: APIView): 19 queryset = super().filter_queryset(request, queryset, view) 20 ordering = queryset.query.order_by 21 if not ordering: 22 return queryset 23 nullable = self._nullable_field_names(queryset) 24 new_ordering = [] 25 changed = False 26 for term in ordering: 27 name = term.lstrip("-") 28 if name in nullable: 29 changed = True 30 if term.startswith("-"): 31 new_ordering.append(F(name).desc(nulls_last=True)) 32 else: 33 new_ordering.append(F(name).asc(nulls_first=True)) 34 else: 35 new_ordering.append(term) 36 return queryset.order_by(*new_ordering) if changed else queryset
class
NullsAwareOrderingFilter(rest_framework.filters.OrderingFilter):
8class NullsAwareOrderingFilter(OrderingFilter): 9 """OrderingFilter that sorts NULL values consistently. 10 11 For any nullable field, NULLs are treated as the smallest possible value: 12 - ascending → NULLs appear first (nulls_first=True) 13 - descending → NULLs appear last (nulls_last=True) 14 """ 15 16 def _nullable_field_names(self, queryset: QuerySet) -> set[str]: 17 return {f.name for f in queryset.model._meta.get_fields() if hasattr(f, "null") and f.null} 18 19 def filter_queryset(self, request: Request, queryset: QuerySet, view: APIView): 20 queryset = super().filter_queryset(request, queryset, view) 21 ordering = queryset.query.order_by 22 if not ordering: 23 return queryset 24 nullable = self._nullable_field_names(queryset) 25 new_ordering = [] 26 changed = False 27 for term in ordering: 28 name = term.lstrip("-") 29 if name in nullable: 30 changed = True 31 if term.startswith("-"): 32 new_ordering.append(F(name).desc(nulls_last=True)) 33 else: 34 new_ordering.append(F(name).asc(nulls_first=True)) 35 else: 36 new_ordering.append(term) 37 return queryset.order_by(*new_ordering) if changed else queryset
OrderingFilter that sorts NULL values consistently.
For any nullable field, NULLs are treated as the smallest possible value:
- ascending → NULLs appear first (nulls_first=True)
- descending → NULLs appear last (nulls_last=True)
def
filter_queryset( self, request: rest_framework.request.Request, queryset: django.db.models.query.QuerySet, view: rest_framework.views.APIView):
19 def filter_queryset(self, request: Request, queryset: QuerySet, view: APIView): 20 queryset = super().filter_queryset(request, queryset, view) 21 ordering = queryset.query.order_by 22 if not ordering: 23 return queryset 24 nullable = self._nullable_field_names(queryset) 25 new_ordering = [] 26 changed = False 27 for term in ordering: 28 name = term.lstrip("-") 29 if name in nullable: 30 changed = True 31 if term.startswith("-"): 32 new_ordering.append(F(name).desc(nulls_last=True)) 33 else: 34 new_ordering.append(F(name).asc(nulls_first=True)) 35 else: 36 new_ordering.append(term) 37 return queryset.order_by(*new_ordering) if changed else queryset
Return a filtered queryset.