mirror of
https://github.com/encode/django-rest-framework.git
synced 2024-11-25 19:14:01 +03:00
Schemas: Improved decimal handling when mapping ChoiceField. (#7264)
This commit is contained in:
parent
603aac7db1
commit
1872bde462
|
@ -330,30 +330,29 @@ class AutoSchema(ViewInspector):
|
||||||
|
|
||||||
def _map_choicefield(self, field):
|
def _map_choicefield(self, field):
|
||||||
choices = list(OrderedDict.fromkeys(field.choices)) # preserve order and remove duplicates
|
choices = list(OrderedDict.fromkeys(field.choices)) # preserve order and remove duplicates
|
||||||
if all(isinstance(choice, bool) for choice in choices):
|
|
||||||
type = 'boolean'
|
|
||||||
elif all(isinstance(choice, int) for choice in choices):
|
|
||||||
type = 'integer'
|
|
||||||
elif all(isinstance(choice, (int, float, Decimal)) for choice in choices): # `number` includes `integer`
|
|
||||||
# Ref: https://tools.ietf.org/html/draft-wright-json-schema-validation-00#section-5.21
|
|
||||||
type = 'number'
|
|
||||||
elif all(isinstance(choice, str) for choice in choices):
|
|
||||||
type = 'string'
|
|
||||||
else:
|
|
||||||
type = None
|
|
||||||
|
|
||||||
mapping = {
|
mapping = {
|
||||||
# The value of `enum` keyword MUST be an array and SHOULD be unique.
|
# The value of `enum` keyword MUST be an array and SHOULD be unique.
|
||||||
# Ref: https://tools.ietf.org/html/draft-wright-json-schema-validation-00#section-5.20
|
# Ref: https://tools.ietf.org/html/draft-wright-json-schema-validation-00#section-5.20
|
||||||
'enum': choices
|
'enum': choices
|
||||||
}
|
}
|
||||||
|
|
||||||
# If We figured out `type` then and only then we should set it. It must be a string.
|
if all(isinstance(choice, bool) for choice in choices):
|
||||||
# Ref: https://swagger.io/docs/specification/data-models/data-types/#mixed-type
|
mapping['type'] = 'boolean'
|
||||||
# It is optional but it can not be null.
|
elif all(isinstance(choice, int) for choice in choices):
|
||||||
|
mapping['type'] = 'integer'
|
||||||
|
elif all(isinstance(choice, Decimal) for choice in choices):
|
||||||
|
mapping['format'] = 'decimal'
|
||||||
|
if api_settings.COERCE_DECIMAL_TO_STRING:
|
||||||
|
mapping['enum'] = [str(choice) for choice in mapping['enum']]
|
||||||
|
mapping['type'] = 'string'
|
||||||
|
else:
|
||||||
|
mapping['type'] = 'number'
|
||||||
|
elif all(isinstance(choice, (int, float, Decimal)) for choice in choices): # `number` includes `integer`
|
||||||
# Ref: https://tools.ietf.org/html/draft-wright-json-schema-validation-00#section-5.21
|
# Ref: https://tools.ietf.org/html/draft-wright-json-schema-validation-00#section-5.21
|
||||||
if type:
|
mapping['type'] = 'number'
|
||||||
mapping['type'] = type
|
elif all(isinstance(choice, str) for choice in choices):
|
||||||
|
mapping['type'] = 'string'
|
||||||
|
|
||||||
return mapping
|
return mapping
|
||||||
|
|
||||||
def _map_field(self, field):
|
def _map_field(self, field):
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import uuid
|
import uuid
|
||||||
import warnings
|
import warnings
|
||||||
|
from decimal import Decimal
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from django.conf.urls import url
|
from django.conf.urls import url
|
||||||
|
@ -78,6 +79,9 @@ class TestFieldMapping(TestCase):
|
||||||
(1, 'One'), (2, 'Two'), (3, 'Three'), (2, 'Two'), (3, 'Three'), (1, 'One'),
|
(1, 'One'), (2, 'Two'), (3, 'Three'), (2, 'Two'), (3, 'Three'), (1, 'One'),
|
||||||
])),
|
])),
|
||||||
{'items': {'enum': [1, 2, 3], 'type': 'integer'}, 'type': 'array'}),
|
{'items': {'enum': [1, 2, 3], 'type': 'integer'}, 'type': 'array'}),
|
||||||
|
(serializers.ListField(child=
|
||||||
|
serializers.ChoiceField(choices=[(Decimal('1.111'), 'one'), (Decimal('2.222'), 'two')])),
|
||||||
|
{'items': {'enum': ['1.111', '2.222'], 'type': 'string', 'format': 'decimal'}, 'type': 'array'}),
|
||||||
(serializers.IntegerField(min_value=2147483648),
|
(serializers.IntegerField(min_value=2147483648),
|
||||||
{'type': 'integer', 'minimum': 2147483648, 'format': 'int64'}),
|
{'type': 'integer', 'minimum': 2147483648, 'format': 'int64'}),
|
||||||
]
|
]
|
||||||
|
@ -85,6 +89,15 @@ class TestFieldMapping(TestCase):
|
||||||
with self.subTest(field=field):
|
with self.subTest(field=field):
|
||||||
assert inspector._map_field(field) == mapping
|
assert inspector._map_field(field) == mapping
|
||||||
|
|
||||||
|
@override_settings(REST_FRAMEWORK={'COERCE_DECIMAL_TO_STRING': False})
|
||||||
|
def test_decimal_schema_for_choice_field(self):
|
||||||
|
inspector = AutoSchema()
|
||||||
|
field = serializers.ListField(
|
||||||
|
child=serializers.ChoiceField(choices=[(Decimal('1.111'), 'one'), (Decimal('2.222'), 'two')]))
|
||||||
|
mapping = {'items': {'enum': [Decimal('1.111'), Decimal('2.222')], 'type': 'number'}, 'type': 'array'}
|
||||||
|
assert inspector._map_field(field) == mapping
|
||||||
|
|
||||||
|
|
||||||
def test_lazy_string_field(self):
|
def test_lazy_string_field(self):
|
||||||
class ItemSerializer(serializers.Serializer):
|
class ItemSerializer(serializers.Serializer):
|
||||||
text = serializers.CharField(help_text=_('lazy string'))
|
text = serializers.CharField(help_text=_('lazy string'))
|
||||||
|
|
Loading…
Reference in New Issue
Block a user