Aaron Lelevier
Aaron Lelevier

Reputation: 20820

Is it possible to do case-insensitive ordering in descending order with the Django ORM

Django has the Lower function, but this only works with ASC (ascending) ordered queries.

Example URL:

'/api/locations/?ordering=-number,name'

We were using the below pattern:

from django.db.models.functions import Lower

class OrderingQuerySetMixin(object):

    def get_queryset(self):
        queryset = self.queryset
        ordering = self.request.query_params.get('ordering', None)
        if ordering:
            if ordering.startswith('-'):
                queryset = queryset.order_by(Lower(ordering[1:])).reverse()
            else:
                queryset = queryset.order_by(Lower(ordering))
        return queryset

The problem with this pattern is it doesn't work for ordering multiple fields. The only way to do this would be to pass the descending argument directly to Lower, and lower fails with anything like '-myfield' like the normal Django ORM does for descending ordering.

Here's the current pattern, which accepts multiple arguments for ordering, but does not handle descending:

class OrderingQuerySetMixin(object):
    """Return a case-insensitive ordered queryset."""

    def eval_param(self, param):
        if param.startswith('-'):
            return param[1:]
        else:
            return param

    def get_queryset(self):
        queryset = self.queryset
        ordering = self.request.query_params.get('ordering', None)
        if ordering:
            queryset = queryset.order_by(
                *[Lower(self.eval_param(p)) for p in ordering.split(',')]
            )
        return queryset

Upvotes: 1

Views: 902

Answers (1)

Ranjan MP
Ranjan MP

Reputation: 381

#Import Lower function
from django.db.models.functions import Lower

#For Ascending order
queryset.order_by(Lower('field_name'))

#For Descending order
queryset.order_by(Lower('field_name').desc())

Upvotes: 2

Related Questions