如何在 ListAPIView 中使用 django-filter 对过滤结果进行排序

Posted

技术标签:

【中文标题】如何在 ListAPIView 中使用 django-filter 对过滤结果进行排序【英文标题】:How to sort the results of filtering with django-filter in a ListAPIView 【发布时间】:2018-08-10 03:42:50 【问题描述】:

我有一个 ListAPIView,它使用 DjangoFilterBackend 根据 url 参数过滤房间。下面的代码可以很好地做到这一点。

现在,我想根据 Room 对象的其他属性、另一个 url 参数以及我们对发出请求的用户的了解,对结果进行排序。函数本身无关紧要。

应用已有的过滤器后,如何对结果进行排序?

如果我自己进行过滤,我想我可以在get_queryset 中进行过滤、计算分数并对结果进行排序,但我不知道在使用django-filter 进行过滤后如何执行此操作。

查询示例

例如,我将执行此查询以按价格低于 100 进行过滤。other_field 值将用于计算排序分数:

http://localhost:8000/api/search/rooms?price=100&other_field=200

代码

class RoomFilter(filters.FilterSet):
    price = filters.NumberFilter(name="price", lookup_expr='lte')
    features = filters.ModelMultipleChoiceFilter(
        name="features", 
        conjoined=True,
        queryset=Features.objects.all()
        )

    class Meta:
        model = Room
        fields = ['price', 'features']

class RoomSearchView(generics.ListAPIView):
    queryset = Room.objects.all()
    serializer_class = RoomSearchSerializer
    filter_backends = (filters.DjangoFilterBackend,)
    filter_class = RoomFilter

【问题讨论】:

【参考方案1】:

尝试在您的 API 中覆盖 list(),如下所示,

class RoomSearchView(generics.ListAPIView):
    queryset = Room.objects.all()
    serializer_class = RoomSearchSerializer
    filter_backends = (filters.DjangoFilterBackend,)
    filter_class = RoomFilter

    def list(self, request, *args, **kwargs):
        queryset = self.filter_queryset(self.get_queryset())

        queryset = queryset.order_by('-id')  # change is here  >> sorted with reverse order of 'id'

        page = self.paginate_queryset(queryset)
        if page is not None:
            serializer = self.get_serializer(page, many=True)
            return self.get_paginated_response(serializer.data)

        serializer = self.get_serializer(queryset, many=True)
        return Response(serializer.data)

【讨论】:

太棒了,它完全符合我的需要!然后我可以在那里进行任何类型的排序,包括添加一个额外的分数列。谢谢!【参考方案2】:

您可以使用 FilterSet 类(在您的情况下为 RoomFilter)以更灵活的方式做到这一点

在 RoomFilter 类中使用 fileds 关键字参数定义一个 OrderFilter

fields是model field name: parameter name的映射。

(https://django-filter.readthedocs.io/en/stable/ref/filters.html)

所以你可以这样做:

class RoomFilter(filters.FilterSet):
price = filters.NumberFilter(name="price", lookup_expr='lte')
features = filters.ModelMultipleChoiceFilter(
    name="features", 
    conjoined=True,
    queryset=Features.objects.all()
    )

 o = OrderingFilter(
    fields=(
        ('id', 'id'),
        ('price', 'price'),
        # and any model field you want to sort based on
    )
)

class Meta:
    model = Room
    fields = ['price', 'features']

根据id升序排序:

http://localhost:8000/api/search/rooms?price=100&other_field=200&o=id

根据id降序排序:

http://localhost:8000/api/search/rooms?price=100&other_field=200&o=-id

【讨论】:

以上是关于如何在 ListAPIView 中使用 django-filter 对过滤结果进行排序的主要内容,如果未能解决你的问题,请参考以下文章

如何将两个不同的ListAPIView转换为单个ModelViewSet

drf 中的ListAPIView视图

django drf GenericAPIView和ListAPIView

(生鲜项目)08. ModelSerializer 实现商品列表页, 使用Mixin来实现返回, 以及更加方便的ListAPIView, 以及分页的设置

DRF的APIView和mixins+GenericAPIView和ListAPIView

Djang视图层