Делайте заказ без учета регистра с помощью django-filter

Можно ли выполнить case-insensitive упорядочивание по first_name с помощью Django-rest-framework.

Вот код:

import django_filter

class PersonFilter(django_filters.FilterSet):
    class Meta:
        model = Person
        fields = ('first_name_lower',)
        order_by = ('first_name_lower',)

class PersonViewSet(BaseModelViewSet):
    queryset = Person.objects.all()
    permission_classes = (permissions.IsAuthenticated,)
    filter_backends = (filters.DjangoFilterBackend,)
    filter_class = PersonFilter

Есть ли простой способ сделать case-insensitive заказ с django-filter?

Здесь django-filter содержит документы для поиска без учета регистра, но ничего для заказа.

В документах Django код для этого несколько тупой, что заставляет меня задаться вопросом, существует ли он для django-filter или нет. Вот фрагмент кода документации Django о том, как это сделать с Django ORM:

>>> from django.db.models.functions import Lower
>>> MyModel.objects.order_by(Lower('myfield'))

person Aaron Lelevier    schedule 06.08.2015    source источник


Ответы (4)


Вы можете создать подкласс OrderingFilter и использовать его как filter_backend, чтобы можно было повторно использовать код.

from rest_framework.filters import OrderingFilter
from django.db.models.functions import Lower

class CaseInsensitiveOrderingFilter(OrderingFilter):
    def filter_queryset(self, request, queryset, view):
        ordering = self.get_ordering(request, queryset, view)

        if ordering is not None:
            if ordering.startswith('-'):
                queryset = queryset.order_by(Lower(ordering[1:])).reverse()
            else:
                queryset = queryset.order_by(Lower(ordering))
        return queryset


class PersonViewSet(ModelViewSet):
    queryset = Person.objects.all()
    serializer_class = MySerializer
    permission_classes = (permissions.IsAuthenticated,)
    filter_backends = (CaseInsensitiveOrderingFilter,)
person zemekeneng    schedule 16.11.2015
comment
Это решение будет работать, но только с одним аргументом ordering. - person Aaron Lelevier; 16.11.2015

Поиск case-insensitive можно выполнить, переопределив метод get_queryset в классе ModelViewSet.

Он работает для по убыванию и по возрастанию case-insensitive порядка.

# Example URL's
'/api/people/?ordering=-first_name'
'/api/people/?ordering=first_name'

Вот код:

class PersonViewSet(ModelViewSet):
    queryset = Person.objects.all()
    serializer_class = MySerializer
    permission_classes = (permissions.IsAuthenticated,)

    def get_queryset(self):
        queryset = self.queryset
        ordering = self.request.query_params.get('ordering', None)
        if ordering is not None:
            from django.db.models.functions import Lower
            if ordering.startswith('-'):
                queryset = queryset.order_by(Lower(ordering[1:])).reverse()
            else:
                queryset = queryset.order_by(Lower(ordering))
        return queryset

from django.db.models.functions import Lower работает только для возрастания, поэтому в основном вызовите .reverse(), если вам нужно перейти по убыванию.

person Aaron Lelevier    schedule 06.08.2015
comment
Просто краткое примечание о вашем последнем предложении, вы действительно можете сделать Lower(ordering).asc() or Lower(ordering).desc(), что позволит смешивать восходящие и нисходящие в сочетании с нижним в разных полях. - person Jonathan Liuti; 16.08.2016
comment
это не удастся, если поле orderby является полем даты и времени или любым нестроковым полем - person Anurag Upadhyaya; 18.12.2018
comment
@JonathanLiuti большое спасибо, я впервые прочитал о такой возможности и решает многие мои текущие проблемы. - person mikicz; 29.11.2019

Вы можете заменить функцию filter функции OrderingFilter и установить параметры упорядочения в Lower:

class CaseInsensitiveOrderingFilter(django_filters.OrderingFilter):
    def filter(self, qs, value):
        if value in django_filters.constants.EMPTY_VALUES:
            return qs

        ordering = [Lower(self.get_ordering_value(param)) for param in value]
        return qs.order_by(*ordering)
person Casper    schedule 25.07.2019

используйте аргументы ordering и ordering_fields в представлении списка django-rest — http://www.django-rest-framework.org/api-guide/filtering/#specifying-a-default-ordering

class UserListView(generics.ListAPIView):
    queryset = User.objects.all()
    serializer_class = UserSerializer
    filter_backends = (filters.OrderingFilter,)
    ordering_fields = ('username', 'email')
    ordering = ('username',)
person Chris Montanaro    schedule 06.08.2015
comment
ordering_fields и т. д.... работает для упорядочения, но не учитывает регистр. Django ORM по умолчанию имеет значение case-sensitive, а приведенный выше аргумент ordering_fields использует функциональность Django ORM по умолчанию для model.objects.order_by. - person Aaron Lelevier; 07.08.2015