mirror of
https://github.com/encode/django-rest-framework.git
synced 2024-11-22 09:36:49 +03:00
Schemas: Add mapping of type for ChoiceField. (#7161)
This commit is contained in:
parent
160f912a60
commit
bc4d52558b
|
@ -1,4 +1,6 @@
|
||||||
import warnings
|
import warnings
|
||||||
|
from collections import OrderedDict
|
||||||
|
from decimal import Decimal
|
||||||
from operator import attrgetter
|
from operator import attrgetter
|
||||||
from urllib.parse import urljoin
|
from urllib.parse import urljoin
|
||||||
|
|
||||||
|
@ -209,6 +211,34 @@ class AutoSchema(ViewInspector):
|
||||||
|
|
||||||
return paginator.get_schema_operation_parameters(view)
|
return paginator.get_schema_operation_parameters(view)
|
||||||
|
|
||||||
|
def _map_choicefield(self, field):
|
||||||
|
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 = {
|
||||||
|
# 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
|
||||||
|
'enum': choices
|
||||||
|
}
|
||||||
|
|
||||||
|
# If We figured out `type` then and only then we should set it. It must be a string.
|
||||||
|
# Ref: https://swagger.io/docs/specification/data-models/data-types/#mixed-type
|
||||||
|
# It is optional but it can not be null.
|
||||||
|
# Ref: https://tools.ietf.org/html/draft-wright-json-schema-validation-00#section-5.21
|
||||||
|
if type:
|
||||||
|
mapping['type'] = type
|
||||||
|
return mapping
|
||||||
|
|
||||||
def _map_field(self, field):
|
def _map_field(self, field):
|
||||||
|
|
||||||
# Nested Serializers, `many` or not.
|
# Nested Serializers, `many` or not.
|
||||||
|
@ -242,15 +272,11 @@ class AutoSchema(ViewInspector):
|
||||||
if isinstance(field, serializers.MultipleChoiceField):
|
if isinstance(field, serializers.MultipleChoiceField):
|
||||||
return {
|
return {
|
||||||
'type': 'array',
|
'type': 'array',
|
||||||
'items': {
|
'items': self._map_choicefield(field)
|
||||||
'enum': list(field.choices)
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if isinstance(field, serializers.ChoiceField):
|
if isinstance(field, serializers.ChoiceField):
|
||||||
return {
|
return self._map_choicefield(field)
|
||||||
'enum': list(field.choices),
|
|
||||||
}
|
|
||||||
|
|
||||||
# ListField.
|
# ListField.
|
||||||
if isinstance(field, serializers.ListField):
|
if isinstance(field, serializers.ListField):
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import uuid
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from django.conf.urls import url
|
from django.conf.urls import url
|
||||||
from django.test import RequestFactory, TestCase, override_settings
|
from django.test import RequestFactory, TestCase, override_settings
|
||||||
|
@ -44,6 +46,8 @@ class TestBasics(TestCase):
|
||||||
|
|
||||||
class TestFieldMapping(TestCase):
|
class TestFieldMapping(TestCase):
|
||||||
def test_list_field_mapping(self):
|
def test_list_field_mapping(self):
|
||||||
|
uuid1 = uuid.uuid4()
|
||||||
|
uuid2 = uuid.uuid4()
|
||||||
inspector = AutoSchema()
|
inspector = AutoSchema()
|
||||||
cases = [
|
cases = [
|
||||||
(serializers.ListField(), {'items': {}, 'type': 'array'}),
|
(serializers.ListField(), {'items': {}, 'type': 'array'}),
|
||||||
|
@ -53,7 +57,25 @@ class TestFieldMapping(TestCase):
|
||||||
(serializers.ListField(child=serializers.IntegerField(max_value=4294967295)),
|
(serializers.ListField(child=serializers.IntegerField(max_value=4294967295)),
|
||||||
{'items': {'type': 'integer', 'maximum': 4294967295, 'format': 'int64'}, 'type': 'array'}),
|
{'items': {'type': 'integer', 'maximum': 4294967295, 'format': 'int64'}, 'type': 'array'}),
|
||||||
(serializers.ListField(child=serializers.ChoiceField(choices=[('a', 'Choice A'), ('b', 'Choice B')])),
|
(serializers.ListField(child=serializers.ChoiceField(choices=[('a', 'Choice A'), ('b', 'Choice B')])),
|
||||||
{'items': {'enum': ['a', 'b']}, 'type': 'array'}),
|
{'items': {'enum': ['a', 'b'], 'type': 'string'}, 'type': 'array'}),
|
||||||
|
(serializers.ListField(child=serializers.ChoiceField(choices=[(1, 'One'), (2, 'Two')])),
|
||||||
|
{'items': {'enum': [1, 2], 'type': 'integer'}, 'type': 'array'}),
|
||||||
|
(serializers.ListField(child=serializers.ChoiceField(choices=[(1.1, 'First'), (2.2, 'Second')])),
|
||||||
|
{'items': {'enum': [1.1, 2.2], 'type': 'number'}, 'type': 'array'}),
|
||||||
|
(serializers.ListField(child=serializers.ChoiceField(choices=[(True, 'true'), (False, 'false')])),
|
||||||
|
{'items': {'enum': [True, False], 'type': 'boolean'}, 'type': 'array'}),
|
||||||
|
(serializers.ListField(child=serializers.ChoiceField(choices=[(uuid1, 'uuid1'), (uuid2, 'uuid2')])),
|
||||||
|
{'items': {'enum': [uuid1, uuid2]}, 'type': 'array'}),
|
||||||
|
(serializers.ListField(child=serializers.ChoiceField(choices=[(1, 'One'), ('a', 'Choice A')])),
|
||||||
|
{'items': {'enum': [1, 'a']}, 'type': 'array'}),
|
||||||
|
(serializers.ListField(child=serializers.ChoiceField(choices=[
|
||||||
|
(1, 'One'), ('a', 'Choice A'), (1.1, 'First'), (1.1, 'First'), (1, 'One'), ('a', 'Choice A'), (1, 'One')
|
||||||
|
])),
|
||||||
|
{'items': {'enum': [1, 'a', 1.1]}, 'type': 'array'}),
|
||||||
|
(serializers.ListField(child=serializers.ChoiceField(choices=[
|
||||||
|
(1, 'One'), (2, 'Two'), (3, 'Three'), (2, 'Two'), (3, 'Three'), (1, 'One'),
|
||||||
|
])),
|
||||||
|
{'items': {'enum': [1, 2, 3], 'type': 'integer'}, 'type': 'array'}),
|
||||||
(serializers.IntegerField(min_value=2147483648),
|
(serializers.IntegerField(min_value=2147483648),
|
||||||
{'type': 'integer', 'minimum': 2147483648, 'format': 'int64'}),
|
{'type': 'integer', 'minimum': 2147483648, 'format': 'int64'}),
|
||||||
]
|
]
|
||||||
|
|
Loading…
Reference in New Issue
Block a user