Django REST framework: how to respond with useful error messages with get_queryset()

巧了我就是萌 提交于 2021-02-08 07:41:36

问题


I have a django model which I want to display via Django Rest framework. I am getting all objects in the model to be displayed via the get_queryset(). However, I also have a couple of query_params which will filter out certain objects. This is my main code which is working fine:

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

with this urlpattern:

path(r'API/plans', views.PlanView.as_view(), name='prices'),

The only problem is that when I purposefully write the below URL in a browser,

http://127.0.0.1:8000/API/plans?size=sm

which has a bad/misspelled query_param value, the get_query() code will just ignore it and display the objects as if there are no filters.

I tried to put an else statement such as:

    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)

but with this, I get an error message saying:

ContentNotRenderedError at /API/plans
The response content must be rendered before it can be iterated over.

How can I display useful error responses/jsons if a user puts in a wrong parameter value in the API?


回答1:


You can use ValidationError

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

DRF catches these exceptions and displays them neatly. It returns a JSON of the form

{
    "detail": "size must be either 'large' or 'small'"
}



回答2:


There are two ways to handle this: manual validation or using the django-filters package which supports DRF natively now.

1) Raise a ValidationError in that case (simplest)

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

2) Use django-filters (best)

Filters help you separate the filtering and sorting logic from your viewset, and remove the need to manually check/parse/validate the incoming data. In the case of a ChoiceFilter (docs) it will also validate the input and raise an error for you.

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

Looking at your schema, if this question is accurate, you could also use the method documented in the filters package or a BooleanFilter if you change the name to is_large, e.g. is_large=True.



来源:https://stackoverflow.com/questions/57842907/django-rest-framework-how-to-respond-with-useful-error-messages-with-get-querys

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!