From 6007bc7d9d6e4b7510927d87c07969ee62e99567 Mon Sep 17 00:00:00 2001 From: Xavier Ordoquy Date: Wed, 23 Sep 2015 17:55:56 +0200 Subject: [PATCH] Min/MaxValue/LengthValidator now defer the message formatting and able to handle new string format. --- rest_framework/compat.py | 56 ++++++++++++++++++++++------------------ rest_framework/fields.py | 27 ++++++++++++------- 2 files changed, 49 insertions(+), 34 deletions(-) diff --git a/rest_framework/compat.py b/rest_framework/compat.py index 460795f65..121b32fb9 100644 --- a/rest_framework/compat.py +++ b/rest_framework/compat.py @@ -11,6 +11,8 @@ from django.conf import settings from django.db import connection, transaction from django.utils import six from django.views.generic import View +from django.core.exceptions import ValidationError + try: import importlib # Available in Python 3.1+ @@ -109,39 +111,43 @@ def get_model_name(model_cls): return model_cls._meta.module_name -# MinValueValidator, MaxValueValidator et al. only accept `message` in 1.8+ -if django.VERSION >= (1, 8): - from django.core.validators import MinValueValidator, MaxValueValidator - from django.core.validators import MinLengthValidator, MaxLengthValidator -else: - from django.core.validators import MinValueValidator as DjangoMinValueValidator - from django.core.validators import MaxValueValidator as DjangoMaxValueValidator - from django.core.validators import MinLengthValidator as DjangoMinLengthValidator - from django.core.validators import MaxLengthValidator as DjangoMaxLengthValidator +from django.core.validators import MinValueValidator +from django.core.validators import MaxValueValidator +from django.core.validators import MinLengthValidator +from django.core.validators import MaxLengthValidator - class MinValueValidator(DjangoMinValueValidator): - def __init__(self, *args, **kwargs): - self.message = kwargs.pop('message', self.message) - super(MinValueValidator, self).__init__(*args, **kwargs) +class CustomValidatorMessage(object): + def __init__(self, *args, **kwargs): + self.message = kwargs.pop('message', self.message) + self.format = kwargs.pop('string_format', '%') + super(CustomValidatorMessage, self).__init__(*args, **kwargs) + + def __call__(self, value): + cleaned = self.clean(value) + params = {'limit_value': self.limit_value, 'show_value': cleaned, 'value': value} + if self.compare(cleaned, self.limit_value): + message = self.message + if self.format == '{': + args = {self.code: self.limit_value} + message = message.format(**args) + raise ValidationError(message, code=self.code, params=params) - class MaxValueValidator(DjangoMaxValueValidator): - def __init__(self, *args, **kwargs): - self.message = kwargs.pop('message', self.message) - super(MaxValueValidator, self).__init__(*args, **kwargs) +class MinValueValidator(CustomValidatorMessage, MinValueValidator): + pass - class MinLengthValidator(DjangoMinLengthValidator): - def __init__(self, *args, **kwargs): - self.message = kwargs.pop('message', self.message) - super(MinLengthValidator, self).__init__(*args, **kwargs) +class MaxValueValidator(CustomValidatorMessage, MaxValueValidator): + pass - class MaxLengthValidator(DjangoMaxLengthValidator): - def __init__(self, *args, **kwargs): - self.message = kwargs.pop('message', self.message) - super(MaxLengthValidator, self).__init__(*args, **kwargs) +class MinLengthValidator(CustomValidatorMessage, MinLengthValidator): + pass + + +class MaxLengthValidator(CustomValidatorMessage, MaxLengthValidator): + pass # URLValidator only accepts `message` in 1.6+ diff --git a/rest_framework/fields.py b/rest_framework/fields.py index 512a73352..5ecc50423 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -675,9 +675,11 @@ class CharField(Field): self.min_length = kwargs.pop('min_length', None) super(CharField, self).__init__(**kwargs) if self.max_length is not None: - self.validators.append(MaxLengthValidator(self.max_length)) + message = self.error_messages['max_length'] + self.validators.append(MaxLengthValidator(self.max_length, message=message, string_format='{')) if self.min_length is not None: - self.validators.append(MinLengthValidator(self.min_length)) + message = self.error_messages['min_length'] + self.validators.append(MinLengthValidator(self.min_length, message=message, string_format='{')) def run_validation(self, data=empty): # Test for the empty string here so that it does not get validated, @@ -818,9 +820,11 @@ class IntegerField(Field): self.min_value = kwargs.pop('min_value', None) super(IntegerField, self).__init__(**kwargs) if self.max_value is not None: - self.validators.append(MaxValueValidator(self.max_value)) + message = self.error_messages['max_value'] + self.validators.append(MaxValueValidator(self.max_value, message=message, string_format='{')) if self.min_value is not None: - self.validators.append(MinValueValidator(self.min_value)) + message = self.error_messages['min_value'] + self.validators.append(MinValueValidator(self.min_value, message=message, string_format='{')) def to_internal_value(self, data): if isinstance(data, six.text_type) and len(data) > self.MAX_STRING_LENGTH: @@ -850,9 +854,11 @@ class FloatField(Field): self.min_value = kwargs.pop('min_value', None) super(FloatField, self).__init__(**kwargs) if self.max_value is not None: - self.validators.append(MaxValueValidator(self.max_value)) + message = self.error_messages['max_value'] + self.validators.append(MaxValueValidator(self.max_value, message=message, string_format='{')) if self.min_value is not None: - self.validators.append(MinValueValidator(self.min_value)) + message = self.error_messages['min_value'] + self.validators.append(MinValueValidator(self.min_value, message=message, string_format='{')) def to_internal_value(self, data): if isinstance(data, six.text_type) and len(data) > self.MAX_STRING_LENGTH: @@ -897,9 +903,11 @@ class DecimalField(Field): super(DecimalField, self).__init__(**kwargs) if self.max_value is not None: - self.validators.append(MaxValueValidator(self.max_value)) + message = self.error_messages['max_value'] + self.validators.append(MaxValueValidator(self.max_value, message=message, string_format='{')) if self.min_value is not None: - self.validators.append(MinValueValidator(self.min_value)) + message = self.error_messages['min_value'] + self.validators.append(MinValueValidator(self.min_value, message=message, string_format='{')) def to_internal_value(self, data): """ @@ -1598,7 +1606,8 @@ class ModelField(Field): max_length = kwargs.pop('max_length', None) super(ModelField, self).__init__(**kwargs) if max_length is not None: - self.validators.append(MaxLengthValidator(max_length)) + message = self.error_messages['max_length'] + self.validators.append(MaxLengthValidator(max_length, message=message, string_format='{')) def to_internal_value(self, data): rel = getattr(self.model_field, 'rel', None)