mirror of
https://github.com/encode/django-rest-framework.git
synced 2025-01-24 16:24:18 +03:00
Add localize keyword argument to DecimalField
(#4233)
Add `localize` keyword argument for DecimalField
This commit is contained in:
parent
b10de37476
commit
6338ce80ab
|
@ -261,9 +261,10 @@ Corresponds to `django.db.models.fields.DecimalField`.
|
||||||
|
|
||||||
- `max_digits` The maximum number of digits allowed in the number. Note that this number must be greater than or equal to decimal_places.
|
- `max_digits` The maximum number of digits allowed in the number. Note that this number must be greater than or equal to decimal_places.
|
||||||
- `decimal_places` The number of decimal places to store with the number.
|
- `decimal_places` The number of decimal places to store with the number.
|
||||||
- `coerce_to_string` Set to `True` if string values should be returned for the representation, or `False` if `Decimal` objects should be returned. Defaults to the same value as the `COERCE_DECIMAL_TO_STRING` settings key, which will be `True` unless overridden. If `Decimal` objects are returned by the serializer, then the final output format will be determined by the renderer.
|
- `coerce_to_string` Set to `True` if string values should be returned for the representation, or `False` if `Decimal` objects should be returned. Defaults to the same value as the `COERCE_DECIMAL_TO_STRING` settings key, which will be `True` unless overridden. If `Decimal` objects are returned by the serializer, then the final output format will be determined by the renderer. Note that setting `localize` will force the value to `True`.
|
||||||
- `max_value` Validate that the number provided is no greater than this value.
|
- `max_value` Validate that the number provided is no greater than this value.
|
||||||
- `min_value` Validate that the number provided is no less than this value.
|
- `min_value` Validate that the number provided is no less than this value.
|
||||||
|
- `localize` Set to `True` to enable localization of input and output based on the current locale. This will also force `coerce_to_string` to `True`. Defaults to `False`. Note that data formatting is enabled if you have set `USE_L10N=True` in your settings file.
|
||||||
|
|
||||||
#### Example usage
|
#### Example usage
|
||||||
|
|
||||||
|
|
|
@ -25,6 +25,7 @@ from django.utils.dateparse import (
|
||||||
)
|
)
|
||||||
from django.utils.duration import duration_string
|
from django.utils.duration import duration_string
|
||||||
from django.utils.encoding import is_protected_type, smart_text
|
from django.utils.encoding import is_protected_type, smart_text
|
||||||
|
from django.utils.formats import localize_input, sanitize_separators
|
||||||
from django.utils.functional import cached_property
|
from django.utils.functional import cached_property
|
||||||
from django.utils.ipv6 import clean_ipv6_address
|
from django.utils.ipv6 import clean_ipv6_address
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
@ -871,6 +872,7 @@ class FloatField(Field):
|
||||||
self.validators.append(MinValueValidator(self.min_value, message=message))
|
self.validators.append(MinValueValidator(self.min_value, message=message))
|
||||||
|
|
||||||
def to_internal_value(self, data):
|
def to_internal_value(self, data):
|
||||||
|
|
||||||
if isinstance(data, six.text_type) and len(data) > self.MAX_STRING_LENGTH:
|
if isinstance(data, six.text_type) and len(data) > self.MAX_STRING_LENGTH:
|
||||||
self.fail('max_string_length')
|
self.fail('max_string_length')
|
||||||
|
|
||||||
|
@ -895,11 +897,15 @@ class DecimalField(Field):
|
||||||
}
|
}
|
||||||
MAX_STRING_LENGTH = 1000 # Guard against malicious string inputs.
|
MAX_STRING_LENGTH = 1000 # Guard against malicious string inputs.
|
||||||
|
|
||||||
def __init__(self, max_digits, decimal_places, coerce_to_string=None, max_value=None, min_value=None, **kwargs):
|
def __init__(self, max_digits, decimal_places, coerce_to_string=None, max_value=None, min_value=None,
|
||||||
|
localize=False, **kwargs):
|
||||||
self.max_digits = max_digits
|
self.max_digits = max_digits
|
||||||
self.decimal_places = decimal_places
|
self.decimal_places = decimal_places
|
||||||
|
self.localize = localize
|
||||||
if coerce_to_string is not None:
|
if coerce_to_string is not None:
|
||||||
self.coerce_to_string = coerce_to_string
|
self.coerce_to_string = coerce_to_string
|
||||||
|
if self.localize:
|
||||||
|
self.coerce_to_string = True
|
||||||
|
|
||||||
self.max_value = max_value
|
self.max_value = max_value
|
||||||
self.min_value = min_value
|
self.min_value = min_value
|
||||||
|
@ -923,7 +929,12 @@ class DecimalField(Field):
|
||||||
Validate that the input is a decimal number and return a Decimal
|
Validate that the input is a decimal number and return a Decimal
|
||||||
instance.
|
instance.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
data = smart_text(data).strip()
|
data = smart_text(data).strip()
|
||||||
|
|
||||||
|
if self.localize:
|
||||||
|
data = sanitize_separators(data)
|
||||||
|
|
||||||
if len(data) > self.MAX_STRING_LENGTH:
|
if len(data) > self.MAX_STRING_LENGTH:
|
||||||
self.fail('max_string_length')
|
self.fail('max_string_length')
|
||||||
|
|
||||||
|
@ -988,6 +999,9 @@ class DecimalField(Field):
|
||||||
|
|
||||||
if not coerce_to_string:
|
if not coerce_to_string:
|
||||||
return quantized
|
return quantized
|
||||||
|
if self.localize:
|
||||||
|
return localize_input(quantized)
|
||||||
|
|
||||||
return '{0:f}'.format(quantized)
|
return '{0:f}'.format(quantized)
|
||||||
|
|
||||||
def quantize(self, value):
|
def quantize(self, value):
|
||||||
|
|
|
@ -5,6 +5,7 @@ from decimal import Decimal
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from django.http import QueryDict
|
from django.http import QueryDict
|
||||||
|
from django.test import TestCase, override_settings
|
||||||
from django.utils import six, timezone
|
from django.utils import six, timezone
|
||||||
|
|
||||||
import rest_framework
|
import rest_framework
|
||||||
|
@ -894,6 +895,22 @@ class TestNoStringCoercionDecimalField(FieldValues):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class TestLocalizedDecimalField(TestCase):
|
||||||
|
@override_settings(USE_L10N=True, LANGUAGE_CODE='pl')
|
||||||
|
def test_to_internal_value(self):
|
||||||
|
field = serializers.DecimalField(max_digits=2, decimal_places=1, localize=True)
|
||||||
|
self.assertEqual(field.to_internal_value('1,1'), Decimal('1.1'))
|
||||||
|
|
||||||
|
@override_settings(USE_L10N=True, LANGUAGE_CODE='pl')
|
||||||
|
def test_to_representation(self):
|
||||||
|
field = serializers.DecimalField(max_digits=2, decimal_places=1, localize=True)
|
||||||
|
self.assertEqual(field.to_representation(Decimal('1.1')), '1,1')
|
||||||
|
|
||||||
|
def test_localize_forces_coerce_to_string(self):
|
||||||
|
field = serializers.DecimalField(max_digits=2, decimal_places=1, coerce_to_string=False, localize=True)
|
||||||
|
self.assertTrue(isinstance(field.to_representation(Decimal('1.1')), six.string_types))
|
||||||
|
|
||||||
|
|
||||||
class TestNoDecimalPlaces(FieldValues):
|
class TestNoDecimalPlaces(FieldValues):
|
||||||
valid_inputs = {
|
valid_inputs = {
|
||||||
'0.12345': Decimal('0.12345'),
|
'0.12345': Decimal('0.12345'),
|
||||||
|
|
Loading…
Reference in New Issue
Block a user