In Django REST Framework, why does raising serializers.ValidationError return errors in different format in validate() and create() methods?
I am working on a DRF project and have a serializer like:
class SomeSerializer(serializers.Serializer):
number_field = serializers.IntegerField(required=False, min_value=25, max_value=100)
In my settings.py, I have
REST_FRAMEWORK = {
other settings....
'EXCEPTION_HANDLER': 'apps.utils.exceptions.custom_exception_handler',
}
In the custom exception handler, I handle ValidationError as follows:
def custom_exception_handler(exc, context):
# Call REST framework's default exception handler first
response = exception_handler(exc, context)
messages = None
code = None
detail = None
if response is not None:
# Map specific exceptions to custom error codes
if isinstance(exc, ValidationError):
code = "validation_error"
detail = "One or more fields failed validation."
messages = exc.detail
status_code = status.HTTP_400_BAD_REQUEST
# After checking for other errors here in between
else:
code = "unexpected_error"
detail = response.data.get("detail", "An unexpected error occurred.")
messages = response.data.get("messages", {"error": str(exc)})
"""
In between the if and else, AuthenticationFailed, PermissionDenied, NotFound, are handled. Add an appropriate code and return in the format
response.data = {
"detail": detail,
"code": code,
"messages": messages
}
"""
The custom exception handler is configured to return the error in the format shown below. But it only happens if I validate the min and max range inside the create() method (without min_value and max_value defined on the serializer field itself)
{
"detail": "One or more fields failed validation.",
"code": "validation_error",
"messages": {
"number_field": [
"Ensure this value is greater than or equal to 25."
]
}
}
But if I leave the validation to min_value and max_value specified on the serializer field, or even if I manually validate it inside validate method, or a validate_number_field method, it is returned in the original DRF format
{
"number_field": [
"Ensure this value is greater than or equal to 25."
]
}
One way to get the error format from the custom exception handler with min_value and max_value defined on the serializer field was to override the to_internal_value method and handle ValidationError there.
def to_internal_value(self, data):
try:
return super().to_internal_value(data)
except serializers.ValidationError as exc:
raise serializers.ValidationError({
"detail": "One or more fields failed validation.",
"code": "validation_error",
"messages": exc.detail
})
But for me, this defeats the purpose of having a custom exception handler. If I want this behaviour project-wide, what options do I have other than subclassing serializers.Serializer (or ModelSerializer) and overriding the to_internal_value method.