Django REST 框架:如何使用 get_queryset() 响应有用的错误消息

use*_*538 1 api django rest http-status-codes django-rest-framework

我有一个 Django 模型,我想通过 Django Rest 框架显示它。我正在通过 来显示模型中的所有对象get_queryset()。不过,我还有一些query_params可以过滤掉某些对象的工具。这是我的主要代码,运行良好:

class PlanView(generics.ListAPIView):
    """
    API endpoint which allows prices to be viewed or edited
    """

    serializer_class = PlanSerializer
    permission_classes = (IsAuthenticatedOrReadOnly,)

    # override method
    def get_queryset(self):
        //get all objects in Plan model
        queryset = Plan.objects.all()

        // possible query parameters to be read from url
        size = self.request.query_params.get("size", None)
        price = self.request.query_params.get("price", None)

        if size is not None:
            if size == "large":
                queryset = queryset.filter(Large=True)
            elif size == "small":
                queryset = queryset.filter(Large=False)

        if price is not None:
            queryset = queryset.filter(price=price)

        return queryset
Run Code Online (Sandbox Code Playgroud)

有了这个urlpattern

path(r'API/plans', views.PlanView.as_view(), name='prices'),
Run Code Online (Sandbox Code Playgroud)

唯一的问题是,当我故意在浏览器中写入以下 URL 时,

http://127.0.0.1:8000/API/plans?size=sm
Run Code Online (Sandbox Code Playgroud)

它具有错误/拼写错误的query_param值, get_query() 代码将忽略它并显示对象,就好像没有过滤器一样。

我尝试添加 else 语句,例如:

    if size is not None:
            if size == "large":
                queryset = queryset.filter(Large=True)
            elif size == "small":
                queryset = queryset.filter(Large=False)
            else:
                return Response({"Error":"bad request"}, status=status.HTTP_400_BAD_REQUEST)
Run Code Online (Sandbox Code Playgroud)

但是这样,我收到一条错误消息:

ContentNotRenderedError at /API/plans
The response content must be rendered before it can be iterated over.
Run Code Online (Sandbox Code Playgroud)

如果用户在 API 中输入了错误的参数值,如何显示有用的错误响应/json?

Bar*_*y12 5

您可以使用ValidationError

from rest_framework.exceptions import ValidationError
# ...
        raise ValidationError(detail="size must be either 'large' or 'small'")
Run Code Online (Sandbox Code Playgroud)

DRF 捕获这些异常并整齐地显示它们。它返回以下形式的 JSON

{
    "detail": "size must be either 'large' or 'small'"
}
Run Code Online (Sandbox Code Playgroud)


And*_*ker 5

有两种方法可以处理此问题:1)手动验证或2)使用django-filters包。

1)手动引发ValidationError(最简单)

if size not in ['small', 'large']:
    raise ValidationError(f"Invalid size {size}.  Please use small/large")
else:
    # filter normally.  remember to consider the '' value
Run Code Online (Sandbox Code Playgroud)

2)使用django过滤器(最好)

过滤器可帮助您将过滤和排序逻辑与视图集分开,并且无需手动检查/解析/验证传入数据。ChoiceFilter在( docs )的情况下,它还会验证输入并为您引发错误。

from django_filters import TypedChoiceFilter
from django_filters.rest_framework import FilterSet, DjangoFilterBackend
from rest_framework.fields import CharField
from rest_framework.generics import ListAPIView
from rest_framework.permissions import AllowAny
from rest_framework.serializers import ModelSerializer


class MyFilter(FilterSet):
    # this will return a 400/validation error if not a or b
    # but it will ignore blank.
    size = TypedChoiceFilter(
        field_name='Large', # your column name was 'Large'
        choices=[('small', 'Small'), ('large', 'Large')],
        convert=lambda value: value == 'large'  # true if value is Large
    )

class MyView(ListAPIView):
    filter_backends = [DjangoFilterBackend]
    filter_class = MyFilter
    ...

    def get_queryset(self):
        return TheModel.objects.all()

Run Code Online (Sandbox Code Playgroud)

查看您的架构,如果这个问题是准确的,您还可以使用过滤器包中记录的方法,或者BooleanFilter如果将名称更改为is_large,例如is_large=True