Skip to content

Commit 1872bde

Browse files
authored
Schemas: Improved decimal handling when mapping ChoiceField. (#7264)
1 parent 603aac7 commit 1872bde

File tree

2 files changed

+30
-18
lines changed

2 files changed

+30
-18
lines changed

rest_framework/schemas/openapi.py

Lines changed: 17 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -330,30 +330,29 @@ def _get_pagination_parameters(self, path, method):
330330

331331
def _map_choicefield(self, field):
332332
choices = list(OrderedDict.fromkeys(field.choices)) # preserve order and remove duplicates
333-
if all(isinstance(choice, bool) for choice in choices):
334-
type = 'boolean'
335-
elif all(isinstance(choice, int) for choice in choices):
336-
type = 'integer'
337-
elif all(isinstance(choice, (int, float, Decimal)) for choice in choices): # `number` includes `integer`
338-
# Ref: https://tools.ietf.org/html/draft-wright-json-schema-validation-00#section-5.21
339-
type = 'number'
340-
elif all(isinstance(choice, str) for choice in choices):
341-
type = 'string'
342-
else:
343-
type = None
344-
345333
mapping = {
346334
# The value of `enum` keyword MUST be an array and SHOULD be unique.
347335
# Ref: https://tools.ietf.org/html/draft-wright-json-schema-validation-00#section-5.20
348336
'enum': choices
349337
}
350338

351-
# If We figured out `type` then and only then we should set it. It must be a string.
352-
# Ref: https://swagger.io/docs/specification/data-models/data-types/#mixed-type
353-
# It is optional but it can not be null.
354-
# Ref: https://tools.ietf.org/html/draft-wright-json-schema-validation-00#section-5.21
355-
if type:
356-
mapping['type'] = type
339+
if all(isinstance(choice, bool) for choice in choices):
340+
mapping['type'] = 'boolean'
341+
elif all(isinstance(choice, int) for choice in choices):
342+
mapping['type'] = 'integer'
343+
elif all(isinstance(choice, Decimal) for choice in choices):
344+
mapping['format'] = 'decimal'
345+
if api_settings.COERCE_DECIMAL_TO_STRING:
346+
mapping['enum'] = [str(choice) for choice in mapping['enum']]
347+
mapping['type'] = 'string'
348+
else:
349+
mapping['type'] = 'number'
350+
elif all(isinstance(choice, (int, float, Decimal)) for choice in choices): # `number` includes `integer`
351+
# Ref: https://tools.ietf.org/html/draft-wright-json-schema-validation-00#section-5.21
352+
mapping['type'] = 'number'
353+
elif all(isinstance(choice, str) for choice in choices):
354+
mapping['type'] = 'string'
355+
357356
return mapping
358357

359358
def _map_field(self, field):

tests/schemas/test_openapi.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import uuid
22
import warnings
3+
from decimal import Decimal
34

45
import pytest
56
from django.conf.urls import url
@@ -78,13 +79,25 @@ def test_list_field_mapping(self):
7879
(1, 'One'), (2, 'Two'), (3, 'Three'), (2, 'Two'), (3, 'Three'), (1, 'One'),
7980
])),
8081
{'items': {'enum': [1, 2, 3], 'type': 'integer'}, 'type': 'array'}),
82+
(serializers.ListField(child=
83+
serializers.ChoiceField(choices=[(Decimal('1.111'), 'one'), (Decimal('2.222'), 'two')])),
84+
{'items': {'enum': ['1.111', '2.222'], 'type': 'string', 'format': 'decimal'}, 'type': 'array'}),
8185
(serializers.IntegerField(min_value=2147483648),
8286
{'type': 'integer', 'minimum': 2147483648, 'format': 'int64'}),
8387
]
8488
for field, mapping in cases:
8589
with self.subTest(field=field):
8690
assert inspector._map_field(field) == mapping
8791

92+
@override_settings(REST_FRAMEWORK={'COERCE_DECIMAL_TO_STRING': False})
93+
def test_decimal_schema_for_choice_field(self):
94+
inspector = AutoSchema()
95+
field = serializers.ListField(
96+
child=serializers.ChoiceField(choices=[(Decimal('1.111'), 'one'), (Decimal('2.222'), 'two')]))
97+
mapping = {'items': {'enum': [Decimal('1.111'), Decimal('2.222')], 'type': 'number'}, 'type': 'array'}
98+
assert inspector._map_field(field) == mapping
99+
100+
88101
def test_lazy_string_field(self):
89102
class ItemSerializer(serializers.Serializer):
90103
text = serializers.CharField(help_text=_('lazy string'))

0 commit comments

Comments
 (0)