From ad436d966fa9ee2f5817aa5c26612c82558c4262 Mon Sep 17 00:00:00 2001 From: Stephan Groß Date: Mon, 15 Apr 2013 12:40:18 +0200 Subject: Add DecimalField support --- rest_framework/fields.py | 75 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) (limited to 'rest_framework/fields.py') diff --git a/rest_framework/fields.py b/rest_framework/fields.py index f3496b53..a1b9f546 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -2,6 +2,7 @@ from __future__ import unicode_literals import copy import datetime +from decimal import Decimal, DecimalException import inspect import re import warnings @@ -721,6 +722,80 @@ class FloatField(WritableField): raise ValidationError(msg) +class DecimalField(WritableField): + type_name = 'DecimalField' + form_field_class = forms.DecimalField + + default_error_messages = { + 'invalid': _('Enter a number.'), + 'max_value': _('Ensure this value is less than or equal to %(limit_value)s.'), + 'min_value': _('Ensure this value is greater than or equal to %(limit_value)s.'), + 'max_digits': _('Ensure that there are no more than %s digits in total.'), + 'max_decimal_places': _('Ensure that there are no more than %s decimal places.'), + 'max_whole_digits': _('Ensure that there are no more than %s digits before the decimal point.') + } + + def __init__(self, max_value=None, min_value=None, max_digits=None, decimal_places=None, *args, **kwargs): + self.max_value, self.min_value = max_value, min_value + self.max_digits, self.decimal_places = max_digits, decimal_places + super(DecimalField, self).__init__(self, *args, **kwargs) + + if max_value is not None: + self.validators.append(validators.MaxValueValidator(max_value)) + if min_value is not None: + self.validators.append(validators.MinValueValidator(min_value)) + + def from_native(self, value): + """ + Validates that the input is a decimal number. Returns a Decimal + instance. Returns None for empty values. Ensures that there are no more + than max_digits in the number, and no more than decimal_places digits + after the decimal point. + """ + if value in validators.EMPTY_VALUES: + return None + value = smart_text(value).strip() + try: + value = Decimal(value) + except DecimalException: + raise ValidationError(self.error_messages['invalid']) + return value + + def to_native(self, value): + if value is not None: + return str(value) + return value + + def validate(self, value): + super(DecimalField, self).validate(value) + if value in validators.EMPTY_VALUES: + return + # Check for NaN, Inf and -Inf values. We can't compare directly for NaN, + # since it is never equal to itself. However, NaN is the only value that + # isn't equal to itself, so we can use this to identify NaN + if value != value or value == Decimal("Inf") or value == Decimal("-Inf"): + raise ValidationError(self.error_messages['invalid']) + sign, digittuple, exponent = value.as_tuple() + decimals = abs(exponent) + # digittuple doesn't include any leading zeros. + digits = len(digittuple) + if decimals > digits: + # We have leading zeros up to or past the decimal point. Count + # everything past the decimal point as a digit. We do not count + # 0 before the decimal point as a digit since that would mean + # we would not allow max_digits = decimal_places. + digits = decimals + whole_digits = digits - decimals + + if self.max_digits is not None and digits > self.max_digits: + raise ValidationError(self.error_messages['max_digits'] % self.max_digits) + if self.decimal_places is not None and decimals > self.decimal_places: + raise ValidationError(self.error_messages['max_decimal_places'] % self.decimal_places) + if self.max_digits is not None and self.decimal_places is not None and whole_digits > (self.max_digits - self.decimal_places): + raise ValidationError(self.error_messages['max_whole_digits'] % (self.max_digits - self.decimal_places)) + return value + + class FileField(WritableField): use_files = True type_name = 'FileField' -- cgit v1.2.3 From 9d80f01bced913dae0859be525b39eaa9df1fdbf Mon Sep 17 00:00:00 2001 From: Stephan Groß Date: Mon, 15 Apr 2013 15:15:55 +0200 Subject: Fix init call --- rest_framework/fields.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'rest_framework/fields.py') diff --git a/rest_framework/fields.py b/rest_framework/fields.py index a1b9f546..6be633db 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -738,7 +738,7 @@ class DecimalField(WritableField): def __init__(self, max_value=None, min_value=None, max_digits=None, decimal_places=None, *args, **kwargs): self.max_value, self.min_value = max_value, min_value self.max_digits, self.decimal_places = max_digits, decimal_places - super(DecimalField, self).__init__(self, *args, **kwargs) + super(DecimalField, self).__init__(*args, **kwargs) if max_value is not None: self.validators.append(validators.MaxValueValidator(max_value)) -- cgit v1.2.3 From cac669702596cdf768971267e6355fb9223a69e8 Mon Sep 17 00:00:00 2001 From: Stephan Groß Date: Mon, 15 Apr 2013 15:24:14 +0200 Subject: Return Decimal instance instead of string --- rest_framework/fields.py | 5 ----- 1 file changed, 5 deletions(-) (limited to 'rest_framework/fields.py') diff --git a/rest_framework/fields.py b/rest_framework/fields.py index 6be633db..926195be 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -761,11 +761,6 @@ class DecimalField(WritableField): raise ValidationError(self.error_messages['invalid']) return value - def to_native(self, value): - if value is not None: - return str(value) - return value - def validate(self, value): super(DecimalField, self).validate(value) if value in validators.EMPTY_VALUES: -- cgit v1.2.3 From 95abe6e8445f59f9e52609b0c54d9276830dbfd3 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Thu, 25 Apr 2013 12:47:34 +0100 Subject: Cleanup docstrings --- rest_framework/fields.py | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'rest_framework/fields.py') diff --git a/rest_framework/fields.py b/rest_framework/fields.py index f3496b53..949f68d6 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -1,3 +1,8 @@ +""" +Serializer fields perform validation on incoming data. + +They are very similar to Django's form fields. +""" from __future__ import unicode_literals import copy -- cgit v1.2.3 From dc7b1d643020cac5d585aac42f98962cc7aa6bf7 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Mon, 29 Apr 2013 12:45:00 +0100 Subject: 2.2's PendingDeprecationWarnings now become DeprecationWarnings. 2.3's PendingDeprecationWarnings added. --- rest_framework/fields.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'rest_framework/fields.py') diff --git a/rest_framework/fields.py b/rest_framework/fields.py index 38fe025d..f934fc39 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -200,9 +200,9 @@ class WritableField(Field): # 'blank' is to be deprecated in favor of 'required' if blank is not None: - warnings.warn('The `blank` keyword argument is due to deprecated. ' + warnings.warn('The `blank` keyword argument is deprecated. ' 'Use the `required` keyword argument instead.', - PendingDeprecationWarning, stacklevel=2) + DeprecationWarning, stacklevel=2) required = not(blank) super(WritableField, self).__init__(source=source) -- cgit v1.2.3