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