Django-rest-framework:对不同的字段有不同的排序过滤器

时间:2017-11-18 08:32:30

标签: django django-rest-framework

我有一个简单的模型:

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

我正在为api端点使用django rest框架。

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 

2 个答案:

答案 0 :(得分:0)

<强> 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)

答案 1 :(得分:0)

我建议为不区分大小写的字段设置特定的类属性

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