From 6bf5db02c0d0cdd75d764629215cc030749df80e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20K=C3=A4ufl?= Date: Tue, 30 Apr 2019 11:27:31 +0200 Subject: [PATCH] [fields] Format error message only if params exist 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 c8f65db0e..37c46024c 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -239,12 +239,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 42adedfed..26448d394 100644 --- a/tests/test_fields.py +++ b/tests/test_fields.py @@ -2230,14 +2230,33 @@ class TestValidationErrorCode: 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',))