mirror of
				https://github.com/encode/django-rest-framework.git
				synced 2025-11-04 09:57:55 +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):
 | 
			
		||||
        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
 | 
			
		||||
        if all(isinstance(choice, bool) for choice in choices):
 | 
			
		||||
            mapping['type'] = 'boolean'
 | 
			
		||||
        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
 | 
			
		||||
            mapping['type'] = 'number'
 | 
			
		||||
        elif all(isinstance(choice, str) for choice in choices):
 | 
			
		||||
            mapping['type'] = 'string'
 | 
			
		||||
 | 
			
		||||
        return mapping
 | 
			
		||||
 | 
			
		||||
    def _map_field(self, field):
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,5 +1,6 @@
 | 
			
		|||
import uuid
 | 
			
		||||
import warnings
 | 
			
		||||
from decimal import Decimal
 | 
			
		||||
 | 
			
		||||
import pytest
 | 
			
		||||
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'),
 | 
			
		||||
            ])),
 | 
			
		||||
                {'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),
 | 
			
		||||
             {'type': 'integer', 'minimum': 2147483648, 'format': 'int64'}),
 | 
			
		||||
        ]
 | 
			
		||||
| 
						 | 
				
			
			@ -85,6 +89,15 @@ class TestFieldMapping(TestCase):
 | 
			
		|||
            with self.subTest(field=field):
 | 
			
		||||
                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):
 | 
			
		||||
        class ItemSerializer(serializers.Serializer):
 | 
			
		||||
            text = serializers.CharField(help_text=_('lazy string'))
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue
	
	Block a user