From 3578bd68837521784e1198141092d15164d73c51 Mon Sep 17 00:00:00 2001 From: Daniel Hahler Date: Fri, 6 Jul 2018 10:44:58 +0200 Subject: [PATCH] get_error_detail: use error_dict/error_list (#5785) --- rest_framework/fields.py | 19 +++++++-- tests/test_fields.py | 91 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 105 insertions(+), 5 deletions(-) diff --git a/rest_framework/fields.py b/rest_framework/fields.py index d6e363339..b32730a80 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -233,10 +233,21 @@ def get_error_detail(exc_info): with the `code` populated. """ code = getattr(exc_info, 'code', None) or 'invalid' - return [ - ErrorDetail(msg, code=code) - for msg in exc_info.messages - ] + + try: + error_dict = exc_info.error_dict + except AttributeError: + return [ + ErrorDetail(error.message % (error.params or ()), + code=error.code if error.code else code) + for error in exc_info.error_list] + return { + k: [ + ErrorDetail(error.message % (error.params or ()), + code=error.code if error.code else code) + for error in errors + ] for k, errors in error_dict.items() + } class CreateOnlyDefault(object): diff --git a/tests/test_fields.py b/tests/test_fields.py index 7227c2f5a..dcd3cfe4c 100644 --- a/tests/test_fields.py +++ b/tests/test_fields.py @@ -6,13 +6,14 @@ import uuid from decimal import ROUND_DOWN, ROUND_UP, Decimal import pytest +from django.core.exceptions import ValidationError as DjangoValidationError from django.http import QueryDict from django.test import TestCase, override_settings from django.utils import six from django.utils.timezone import activate, deactivate, override, utc import rest_framework -from rest_framework import compat, serializers +from rest_framework import compat, exceptions, serializers from rest_framework.fields import DjangoImageField, is_simple_callable try: @@ -2183,3 +2184,91 @@ class TestSerializerMethodField: "'ExampleSerializer', because it is the same as the default " "method name. Remove the `method_name` argument." ) + + +class TestValidationErrorCode: + @pytest.mark.parametrize('use_list', (False, True)) + def test_validationerror_code_with_msg(self, use_list): + + class ExampleSerializer(serializers.Serializer): + password = serializers.CharField() + + def validate_password(self, obj): + err = DjangoValidationError('exc_msg', code='exc_code') + if use_list: + err = DjangoValidationError([err]) + raise err + + serializer = ExampleSerializer(data={'password': 123}) + serializer.is_valid() + assert serializer.errors == {'password': ['exc_msg']} + assert serializer.errors['password'][0].code == 'exc_code' + + @pytest.mark.parametrize('code', (None, 'exc_code',)) + @pytest.mark.parametrize('use_list', (False, True)) + def test_validationerror_code_with_dict(self, use_list, code): + + class ExampleSerializer(serializers.Serializer): + + def validate(self, obj): + if code is None: + err = DjangoValidationError({ + 'email': 'email error', + }) + else: + err = DjangoValidationError({ + 'email': DjangoValidationError( + 'email error', + code=code), + }) + if use_list: + err = DjangoValidationError([err]) + raise err + + serializer = ExampleSerializer(data={}) + serializer.is_valid() + expected_code = code if code else 'invalid' + if use_list: + assert serializer.errors == { + 'non_field_errors': [ + exceptions.ErrorDetail( + string='email error', + code=expected_code + ) + ] + } + else: + assert serializer.errors == { + 'email': ['email error'], + } + assert serializer.errors['email'][0].code == expected_code + + @pytest.mark.parametrize('code', (None, 'exc_code',)) + def test_validationerror_code_with_dict_list_same_code(self, code): + + class ExampleSerializer(serializers.Serializer): + + def validate(self, obj): + if code is None: + raise DjangoValidationError({'email': ['email error 1', + 'email error 2']}) + raise DjangoValidationError({'email': [ + DjangoValidationError('email error 1', code=code), + DjangoValidationError('email error 2', code=code), + ]}) + + serializer = ExampleSerializer(data={}) + serializer.is_valid() + expected_code = code if code else 'invalid' + assert serializer.errors == { + 'email': [ + exceptions.ErrorDetail( + string='email error 1', + code=expected_code + ), + exceptions.ErrorDetail( + string='email error 2', + code=expected_code + ), + ] + }