【问题标题】:django filter on APIViewAPIView 上的 Django 过滤器
【发布时间】:2017-10-09 21:58:18
【问题描述】:

我有一个 APIView 类,用于显示所有租金以及发布和删除等。现在我想要搜索功能,所以我尝试使用 DjangoFilterBackend 但它不起作用。我在文档中看到,它已与 ListAPIView 一起使用,但我如何在 APIView 中使用它。

class Rent(APIView):
    """
    List all the rents if token is not provided else a token specific rent
    """
    serializer_class = RentSerializer
    filter_backends = (DjangoFilterBackend,)
    filter_fields = ('city', 'place', 'property_category',)
    search_fields = ('=city', '=place')
    def get(self, request, token=None, format=None):
        reply={}
        try:
            rents = Rental.objects.all()
            if token:
                rent = Rental.objects.get(token=token)
                reply['data'] = self.serializer_class(rent).data
            else:
                reply['data'] = self.serializer_class(rents, many=True).data
        except Rental.DoesNotExist:
            return error.RequestedResourceNotFound().as_response()
        except:
            return error.UnknownError().as_response()
        else:
            return Response(reply, status.HTTP_200_OK)

当我在 url 中使用以下参数搜索租金时,我会得到所有租金,相反,我应该只获得位于加德满都市的租金并放置 koteshwor

http://localhost:8000/api/v1/rents?city=Kathmandu&place=Koteshwor

【问题讨论】:

  • 既然这个视图的目的似乎是返回一个对象列表,为什么不直接使用ListAPIView
  • 我已经发布,更新,删除此视图上的所有内容,并显示单个租金或所有租金已通过单个 get 函数完成,而不是使用 ListAPIView 和 RetrieveAPIView
  • 所以您已经复制了 DRF 视图集的功能。
  • 我相信过滤器只适用于ViewSets。由于您使用APIView 手动编写所有内容,因此您必须手动实现过滤。顺便说一句,当您手动生成 QuerySet 并返回 JSON 响应时,您认为 DjangoFilterBackend 会做什么以及它如何为您神奇地填充过滤器?你需要仔细阅读 DRF 文档,他们有一个很好的教程,解释了每一个细微差别。
  • @EugeneMorozov,文档显示过滤器使用ListAPIView,正如操作所说。 django-rest-framework.org/api-guide/filtering/…

标签: python django django-rest-framework django-filter


【解决方案1】:

要使用 DjangoFilterBackend 的功能,您可以合并来自 GenericViewSetfilter_queryset 方法,这是从 APIView 继承的 DRF 类,并导致 DRF 中所有特定的“通用”视图类。它看起来像这样:

def filter_queryset(self, queryset):
    """
    Given a queryset, filter it with whichever filter backend is in use.
    You are unlikely to want to override this method, although you may need
    to call it either from a list view, or from a custom `get_object`
    method if you want to apply the configured filtering backend to the
    default queryset.
    """
    for backend in list(self.filter_backends):
        queryset = backend().filter_queryset(self.request, queryset, self)
    return queryset

https://github.com/encode/django-rest-framework/blob/master/rest_framework/generics.py

【讨论】:

  • 这应该是公认的答案。它最大限度地减少了实现所需结果所需的开销。
【解决方案2】:

这里如果你使用APIView,和过滤器没有关系。所以你必须这样做

get_data = request.query_params #or request.GET check both

然后

Rental.objects.filter(city=get_data['city'], place=get_data['place'])

【讨论】:

  • 如果您使用ListAPIView 进行过滤,drf 无论如何都只使用这种 django ORM 方法。
  • 我偏爱 APIView 的原因是我们必须手动完成所有工作。这将帮助我越来越多地学习这个概念。我同意你给我看的方式。谢谢你的帮助。再问一个问题,rest_framework中的setup_eager_loading你知道吗?
  • 如果过滤器都是可选的呢?
【解决方案3】:

如果有人想知道我们如何将 django_filters filter_class 与 api_views 集成:

@api_view(['GET'])
@permission_classes([permissions.IsAuthenticated])
def filter_data(request, format=None):
    qs = models.YourModal.objects.all()

    filtered_data = filters.YourFilter(request.GET, queryset=qs)
    filtered_qs = filtered_data.qs
    ....
    return response.Ok(yourData)

【讨论】:

【解决方案4】:

添加到@ChidG 的答案。您需要做的就是覆盖 DjangoFilterBackend 的 filter_queryset 方法,它是过滤器的入口点,并将 APIView 的实例传递给它。这里要注意的重要一点是,您必须在视图上声明 filter_fieldsfilter_class 才能使过滤器工作。否则它只会返回未过滤的查询集。

如果您对它的工作原理更加好奇,该课程位于django_filters.rest_framework.backends.py

在本例中,网址类似于{base_url}/foo?is_active=true

from django_filters.rest_framework import DjangoFilterBackend

class FooFilter(DjangoFilterBackend):

    def filter_queryset(self, request, queryset, view):
        filter_class = self.get_filter_class(view, queryset)

        if filter_class:
            return filter_class(request.query_params, queryset=queryset, request=request).qs
        return queryset

class Foo(APIView):

    permission_classes = (AllowAny,)
    filter_fields = ('name', 'is_active')

    def get(self, request, format=None):
        queryset = Foo.objects.all()

        ff = FooFilter()
        filtered_queryset = ff.filter_queryset(request, queryset, self)

        if filtered_queryset.exists():
            serializer = FooSerializer(queryset, many=True)
            return Response(serializer.data, status=status.HTTP_200_OK)
        else:
            return Response([], status=status.HTTP_200_OK)

【讨论】:

    猜你喜欢
    • 2020-06-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-07-07
    • 1970-01-01
    • 2021-04-29
    • 2014-01-30
    相关资源
    最近更新 更多