From d1b8511f918443f95d30ed0aa83736239bd691a0 Mon Sep 17 00:00:00 2001 From: kieffer julien Date: Fri, 28 Aug 2020 11:24:59 +0200 Subject: [PATCH] Adding params to error details --- rest_framework/exceptions.py | 21 ++++++++++++++++----- rest_framework/fields.py | 7 +++++-- tests/test_validation_error.py | 17 +++++++++++------ 3 files changed, 32 insertions(+), 13 deletions(-) diff --git a/rest_framework/exceptions.py b/rest_framework/exceptions.py index 345a40524..71419b54a 100644 --- a/rest_framework/exceptions.py +++ b/rest_framework/exceptions.py @@ -38,7 +38,8 @@ def _get_error_details(data, default_code=None): text = force_str(data) code = getattr(data, 'code', default_code) - return ErrorDetail(text, code) + params = getattr(data, 'params', tuple()) + return ErrorDetail(text, code, params) def _get_codes(detail): @@ -54,10 +55,16 @@ def _get_full_details(detail): return [_get_full_details(item) for item in detail] elif isinstance(detail, dict): return {key: _get_full_details(value) for key, value in detail.items()} - return { + + _base = { 'message': detail, 'code': detail.code } + params = getattr(detail, "params", None) + + if params: + _base.update({"params": params}) + return _base class ErrorDetail(str): @@ -65,10 +72,12 @@ class ErrorDetail(str): A string-like object that can additionally have a code. """ code = None + params = None - def __new__(cls, string, code=None): + def __new__(cls, string, code=None, params=None): self = super().__new__(cls, string) self.code = code + self.params = params return self def __eq__(self, other): @@ -82,11 +91,13 @@ class ErrorDetail(str): return not self.__eq__(other) def __repr__(self): - return 'ErrorDetail(string=%r, code=%r)' % ( + base = 'ErrorDetail(string=%r, code=%r' % ( str(self), self.code, ) + return base + ', params=%r)' % self.params if self.params else base + ')' + def __hash__(self): return hash(str(self)) @@ -105,7 +116,6 @@ class APIException(Exception): detail = self.default_detail if code is None: code = self.default_code - self.detail = _get_error_details(detail, code) def __str__(self): @@ -141,6 +151,7 @@ class ValidationError(APIException): default_code = 'invalid' def __init__(self, detail=None, code=None): + print(detail, code) if detail is None: detail = self.default_detail if code is None: diff --git a/rest_framework/fields.py b/rest_framework/fields.py index da2dd54be..0b8a0a623 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -236,12 +236,14 @@ def get_error_detail(exc_info): except AttributeError: return [ ErrorDetail((error.message % error.params) if error.params else error.message, - code=error.code if error.code else code) + code=error.code if error.code else code, + params=error.params) for error in exc_info.error_list] return { k: [ ErrorDetail((error.message % error.params) if error.params else error.message, - code=error.code if error.code else code) + code=error.code if error.code else code, + params=error.params) for error in errors ] for k, errors in error_dict.items() } @@ -599,6 +601,7 @@ class Field: raise errors.extend(exc.detail) except DjangoValidationError as exc: + print("YOOOOOOOOOO", exc, exc.code, exc.params) errors.extend(get_error_detail(exc)) if errors: raise ValidationError(errors) diff --git a/tests/test_validation_error.py b/tests/test_validation_error.py index 562fe37e6..7db89dabd 100644 --- a/tests/test_validation_error.py +++ b/tests/test_validation_error.py @@ -12,17 +12,17 @@ factory = APIRequestFactory() class ExampleSerializer(serializers.Serializer): char = serializers.CharField() - integer = serializers.IntegerField() + integer = serializers.IntegerField(min_value=1, required=False) class ErrorView(APIView): def get(self, request, *args, **kwargs): - ExampleSerializer(data={}).is_valid(raise_exception=True) + ExampleSerializer(data={"integer": 0}).is_valid(raise_exception=True) @api_view(['GET']) def error_view(request): - ExampleSerializer(data={}).is_valid(raise_exception=True) + ExampleSerializer(data={"integer": 0}).is_valid(raise_exception=True) class TestValidationErrorWithFullDetails(TestCase): @@ -41,8 +41,13 @@ class TestValidationErrorWithFullDetails(TestCase): 'code': 'required', }], 'integer': [{ - 'message': 'This field is required.', - 'code': 'required' + 'message': 'Ensure this value is greater than or equal to 1.', + 'code': 'min_value', + 'params': { + 'limit_value': 1, + 'show_value': 0, + 'value': 0 + } }], } @@ -78,7 +83,7 @@ class TestValidationErrorWithCodes(TestCase): self.expected_response_data = { 'char': ['required'], - 'integer': ['required'], + 'integer': ['min_value'], } def tearDown(self):