Django-rest-framework: иметь разные упорядоченные фильтры для разных полей

У меня есть простая модель ниже:

class Ingredient(models.Model):
    name = models.CharField(max_length=30)

Я использую django остальные рамки для конечных точек API.

class IngredientListAPIView(ListAPIView):
    queryset = Ingredient.objects.all()
    serializer_class = IngredientListSerializer
    filter_backends = [OrderingFilter]

Я хотел, чтобы мои две конечные точки выводились как:

?ordering=name  -- i want the ordering to be case-insensitive
?ordering=-name  -- i want the ordering to be case-insensitive

единственный способ добиться этого - создать

class CaseInsensitiveOrderingFilter(OrderingFilter):

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

        if ordering:
            new_ordering = []
            for field in ordering:
                if field.startswith('-'):
                    new_ordering.append(Lower(field[1:]).desc())
                else:
                    new_ordering.append(Lower(field).asc())
            return queryset.order_by(*new_ordering)

        return queryset

а потом

class IngredientListAPIView(ListAPIView):
    queryset = Ingredient.objects.all().order_by(Lower('name'))
    serializer_class = IngredientListSerializer
    filter_backends = [CaseInsensitiveOrderingFilter]

Но теперь, когда я получаю доступ к следующим конечным точкам

?ordering=id  -- it shows 1,10,11,12
?ordering=-id  -- it shows 99,98 ..100..

Если я использую filter_backends = [OrderingFilter] вместо filter_backends = [CaseInsensitiveOrderingFilter]

?ordering=id  -- it shows 1,2,3,4,
?ordering=-id  -- it shows 220,221,220

так как сказать Django использовать

filter_backends = [CaseInsensitiveOrderingFilter] for name field and
filter_backends = [OrderingFilter] for id field 

3 ответа

Решение

Я предлагаю иметь определенный атрибут класса для нечувствительных к регистру полей

class IngredientListAPIView(ListAPIView):
    queryset = Ingredient.objects.all().order_by(Lower('name'))
    serializer_class = IngredientListSerializer
    filter_backends = [CaseInsensitiveOrderingFilter]
    ordering_fields = () # include both normal and case insensitive fields
    ordering_case_insensitive_fields = () # put here only case insensitive fields

Тогда ваш класс заказа будет:

class CaseInsensitiveOrderingFilter(OrderingFilter):

    def filter_queryset(self, request, queryset, view):
        ordering = self.get_ordering(request, queryset, view)
        insensitive_ordering = getattr(view, 'ordering_case_insensitive_fields', ())

        if ordering:
            new_ordering = []
            for field in ordering:
                if field in insensitive_ordering:
                    new_ordering.append(Lower(field[1:]).desc() if field.startswith('-') else Lower(field).asc())
                else:
                    new_ordering.append(field)
            return queryset.order_by(*new_ordering)

        return queryset

views.py

class IngredientListAPIView(ListAPIView):
    queryset = Ingredient.objects.all()
    serializer_class = IngredientListSerializer

    def filter_queryset(self, queryset):
        if "name" in self.request.query_params.get("ordering"):
            return CaseInsensitiveOrderingFilter().filter_queryset(self.request, queryset, self)
        else:
            queryset = OrderingFilter().filter_queryset(self.request, queryset, self)
            return SearchFilter().filter_queryset(self.request, queryset, self)

Вот фиксированный класс порядка из ответа @Gabriel Muj

      class MyOrderingFilter(filters.OrderingFilter):
    def filter_queryset(self, request, queryset, view):
        ordering = self.get_ordering(request, queryset, view)
        insensitive_ordering = getattr(view, 'ordering_case_insensitive_fields', ())

        if ordering:
            new_ordering = []
            for field in ordering:
                if field in insensitive_ordering or (field.startswith('-') and field[1:] in insensitive_ordering):
                    # Use case insensitive ordering for listed fields
                    new_ordering.append(Lower(field[1:]).desc() if field.startswith('-') else Lower(field).asc())
                else:
                    new_ordering.append(field)
            return queryset.order_by(*new_ordering)

        return queryset
Другие вопросы по тегам