From c42df551518311ad62c5d8ba87b307ef137050a4 Mon Sep 17 00:00:00 2001 From: Daniel Hahler Date: Thu, 3 May 2018 15:52:50 +0200 Subject: [PATCH] fix and more tests --- rest_framework/fields.py | 32 +++++++++++++++--- tests/test_fields.py | 73 ++++++++++++++++++++++++++++++++++++++-- 2 files changed, 98 insertions(+), 7 deletions(-) diff --git a/rest_framework/fields.py b/rest_framework/fields.py index 9438fcf089c..4ed81b1e298 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -227,17 +227,39 @@ def __init__(self, value, display_text, disabled=False): yield Option(value='n/a', display_text=cutoff_text, disabled=True) +def _get_error_detail_dict(error_dict, default_code): + return { + k: [ + ErrorDetail(error.message % (error.params or ()), + code=error.code if error.code else default_code) + for error in errors + ] for k, errors in error_dict.items() + } + + def get_error_detail(exc_info): """ Given a Django ValidationError, return a list of ErrorDetail, with the `code` populated. """ code = getattr(exc_info, 'code', None) or 'invalid' - return [ - ErrorDetail(error.message % (error.params or ()), - code=error.code if error.code else code) - for error in exc_info.error_list - ] + + try: + error_dict = exc_info.error_dict + except AttributeError: + errors = [] + for error in exc_info.error_list: + try: + error_dict = error.error_dict + except AttributeError: + detail = ErrorDetail(error.message % (error.params or ()), + code=error.code if error.code else code) + else: + detail = _get_error_detail_dict(error, code) + errors.append(detail) + return errors + else: + return _get_error_detail_dict(error_dict, code) class CreateOnlyDefault(object): diff --git a/tests/test_fields.py b/tests/test_fields.py index 57db6dad33d..dcd3cfe4c8a 100644 --- a/tests/test_fields.py +++ b/tests/test_fields.py @@ -13,7 +13,7 @@ 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: @@ -2188,7 +2188,7 @@ class ExampleSerializer(serializers.Serializer): class TestValidationErrorCode: @pytest.mark.parametrize('use_list', (False, True)) - def test_validationerror_code(self, use_list): + def test_validationerror_code_with_msg(self, use_list): class ExampleSerializer(serializers.Serializer): password = serializers.CharField() @@ -2203,3 +2203,72 @@ def validate_password(self, obj): 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 + ), + ] + }