问题
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