From cbebd9317d6f25f0155275b58e77140fbdc96191 Mon Sep 17 00:00:00 2001 From: Michael Date: Mon, 1 Jul 2019 12:28:16 +0000 Subject: [PATCH] [fields] Format error message only if params exist (#6624) This prevents exceptions when the error message contains `%`, but is not intended for formatting. Django itself does the same: https://github.com/django/django/blob/6866c91b638de5368c18713fa851bfe56253ea55/django/core/exceptions.py#L168-L169 Fixes encode/django-rest-framework#6622 --- rest_framework/fields.py | 4 ++-- tests/test_fields.py | 23 +++++++++++++++++++++-- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/rest_framework/fields.py b/rest_framework/fields.py index 571c145579d..77767b2c5f9 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -231,12 +231,12 @@ def get_error_detail(exc_info): error_dict = exc_info.error_dict except AttributeError: return [ - ErrorDetail(error.message % (error.params or ()), + ErrorDetail((error.message % error.params) if error.params else error.message, code=error.code if error.code else code) for error in exc_info.error_list] return { k: [ - ErrorDetail(error.message % (error.params or ()), + ErrorDetail((error.message % error.params) if error.params else error.message, code=error.code if error.code else code) for error in errors ] for k, errors in error_dict.items() diff --git a/tests/test_fields.py b/tests/test_fields.py index ba516403143..9711367476a 100644 --- a/tests/test_fields.py +++ b/tests/test_fields.py @@ -2255,14 +2255,33 @@ class ExampleSerializer(serializers.Serializer): password = serializers.CharField() def validate_password(self, obj): - err = DjangoValidationError('exc_msg', code='exc_code') + err = DjangoValidationError( + 'exc_msg %s', code='exc_code', params=('exc_param',), + ) 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': ['exc_msg exc_param']} + assert serializer.errors['password'][0].code == 'exc_code' + + @pytest.mark.parametrize('use_list', (False, True)) + def test_validationerror_code_with_msg_including_percent(self, use_list): + + class ExampleSerializer(serializers.Serializer): + password = serializers.CharField() + + def validate_password(self, obj): + err = DjangoValidationError('exc_msg with %', 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 with %']} assert serializer.errors['password'][0].code == 'exc_code' @pytest.mark.parametrize('code', (None, 'exc_code',))