Как поднять 404 в качестве кода состояния в функции Serializer Validate в DRF?
Я написал функцию validate() внутри моего сериализатора. По умолчанию Serializer Errors возвращает 400 в качестве кода состояния. Но я хочу возвращать 404. Я попробовал следующее:
class MySerializer(serializers.ModelSerializer):
class Meta:
model = models.MyClass
fields = "__all__"
def validate(self, data):
current_user = self.context.get("request").user
user = data.get("user")
if user!=current_user:
raise ValidationError({'detail': 'Not found.'}, code=404)
return data
Но он по-прежнему возвращает 400 в качестве ответа в коде состояния. Как это сделать?
Это исходный код Django, который обрабатывает ошибки валидации.
def is_valid(self, *, raise_exception=False):
# This implementation is the same as the default,
# except that we use lists, rather than dicts, as the empty case.
assert hasattr(self, 'initial_data'), (
'Cannot call `.is_valid()` as no `data=` keyword argument was '
'passed when instantiating the serializer instance.'
)
if not hasattr(self, '_validated_data'):
try:
self._validated_data = self.run_validation(self.initial_data)
except ValidationError as exc:
self._validated_data = []
self._errors = exc.detail
else:
self._errors = []
if self._errors and raise_exception:
raise ValidationError(self.errors)
return not bool(self._errors)
Django проверяет наличие ValidationError, затем берет детали из этого объекта ошибки и пересоздает с кодом состояния 400, поэтому ваш код не работает.
Итак, из класса представления поймайте ошибку django и измените код состояния, как показано на рисунке.
class NotesList(mixins.CreateModelMixin, mixins.ListModelMixin, generics.GenericAPIView):
serializer_class = MySerializer
def get_queryset(self):
return models.MyClass.objects.filter(owner=self.request.user)
def post(self, request):
try:
return self.create(request)
except ValidationError as exc:
exc.status_code = 404
raise exc
Вы можете сделать это из представления, обработав валидацию сериализации:
class MySerializer(serializers.ModelSerializer):
class Meta:
model = models.MyClass
fields = "__all__"
def validate(self, data):
current_user = self.context.get("request").user
user = data.get("user")
if user != current_user:
raise serializers.ValidationError(
{'detail': 'Not found.'},
code=404,
)
return data
class MyView(APIView):
def post(self, request):
serializer = MySerializer(
data=request.data,
context={'request': request},
)
if not serializer.is_valid():
return Response(serializer.errors, status=404)
return Response(serializer.data)
Альтернативно вы можете сделать это, определив собственный обработчик исключений:
def my_exception_handler(exc, context):
response = exception_handler(exc, context)
if response is not None and response.status_code == 400:
response.status_code = 404
return response
class MyView(APIView):
exception_handler = my_exception_handler
def post(self, request):
serializer = MySerializer(
data=request.data,
context={'request': request},
)
serializer.is_valid(raise_exception=True)
return Response(serializer.data)
Но как вы сказали, если вы хотите сделать это из сериализатора, то вы должны определить пользовательское исключение и поднять его из сериализатора :
class NotFoundError(serializers.ValidationError):
def __init__(self, detail):
super().__init__(detail, code=404)
class MySerializer(serializers.ModelSerializer):
class Meta:
model = models.MyClass
fields = "__all__"
def validate(self, data):
current_user = self.context.get("request").user
user = data.get("user")
if user != current_user:
raise NotFoundError({'detail': 'Not found.'})
return data