mirror of
				https://github.com/encode/django-rest-framework.git
				synced 2025-10-31 16:07:38 +03:00 
			
		
		
		
	Fix mapping for choice values (#8968)
* fix mapping for choice values * fix tests for ChoiceField IntegerChoices * fix imports * fix imports in tests * Check for multiple choice enums * fix formatting * add tests for text choices class
This commit is contained in:
		
							parent
							
								
									d14eb7555d
								
							
						
					
					
						commit
						e08e606c82
					
				|  | @ -16,6 +16,7 @@ from django.core.validators import ( | ||||||
|     MinValueValidator, ProhibitNullCharactersValidator, RegexValidator, |     MinValueValidator, ProhibitNullCharactersValidator, RegexValidator, | ||||||
|     URLValidator, ip_address_validators |     URLValidator, ip_address_validators | ||||||
| ) | ) | ||||||
|  | from django.db.models import IntegerChoices, TextChoices | ||||||
| from django.forms import FilePathField as DjangoFilePathField | from django.forms import FilePathField as DjangoFilePathField | ||||||
| from django.forms import ImageField as DjangoImageField | from django.forms import ImageField as DjangoImageField | ||||||
| from django.utils import timezone | from django.utils import timezone | ||||||
|  | @ -1397,6 +1398,10 @@ class ChoiceField(Field): | ||||||
|         if data == '' and self.allow_blank: |         if data == '' and self.allow_blank: | ||||||
|             return '' |             return '' | ||||||
| 
 | 
 | ||||||
|  |         if isinstance(data, (IntegerChoices, TextChoices)) and str(data) != \ | ||||||
|  |                 str(data.value): | ||||||
|  |             data = data.value | ||||||
|  | 
 | ||||||
|         try: |         try: | ||||||
|             return self.choice_strings_to_values[str(data)] |             return self.choice_strings_to_values[str(data)] | ||||||
|         except KeyError: |         except KeyError: | ||||||
|  | @ -1405,6 +1410,11 @@ class ChoiceField(Field): | ||||||
|     def to_representation(self, value): |     def to_representation(self, value): | ||||||
|         if value in ('', None): |         if value in ('', None): | ||||||
|             return value |             return value | ||||||
|  | 
 | ||||||
|  |         if isinstance(value, (IntegerChoices, TextChoices)) and str(value) != \ | ||||||
|  |                 str(value.value): | ||||||
|  |             value = value.value | ||||||
|  | 
 | ||||||
|         return self.choice_strings_to_values.get(str(value), value) |         return self.choice_strings_to_values.get(str(value), value) | ||||||
| 
 | 
 | ||||||
|     def iter_options(self): |     def iter_options(self): | ||||||
|  | @ -1428,7 +1438,8 @@ class ChoiceField(Field): | ||||||
|         # Allows us to deal with eg. integer choices while supporting either |         # Allows us to deal with eg. integer choices while supporting either | ||||||
|         # integer or string input, but still get the correct datatype out. |         # integer or string input, but still get the correct datatype out. | ||||||
|         self.choice_strings_to_values = { |         self.choice_strings_to_values = { | ||||||
|             str(key): key for key in self.choices |             str(key.value) if isinstance(key, (IntegerChoices, TextChoices)) | ||||||
|  |             and str(key) != str(key.value) else str(key): key for key in self.choices | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|     choices = property(_get_choices, _set_choices) |     choices = property(_get_choices, _set_choices) | ||||||
|  |  | ||||||
|  | @ -5,11 +5,13 @@ import re | ||||||
| import sys | import sys | ||||||
| import uuid | import uuid | ||||||
| from decimal import ROUND_DOWN, ROUND_UP, Decimal | from decimal import ROUND_DOWN, ROUND_UP, Decimal | ||||||
|  | from enum import auto | ||||||
| from unittest.mock import patch | from unittest.mock import patch | ||||||
| 
 | 
 | ||||||
| import pytest | import pytest | ||||||
| import pytz | import pytz | ||||||
| from django.core.exceptions import ValidationError as DjangoValidationError | from django.core.exceptions import ValidationError as DjangoValidationError | ||||||
|  | from django.db.models import IntegerChoices, TextChoices | ||||||
| from django.http import QueryDict | from django.http import QueryDict | ||||||
| from django.test import TestCase, override_settings | from django.test import TestCase, override_settings | ||||||
| from django.utils.timezone import activate, deactivate, override | from django.utils.timezone import activate, deactivate, override | ||||||
|  | @ -1824,6 +1826,54 @@ class TestChoiceField(FieldValues): | ||||||
|             field.run_validation(2) |             field.run_validation(2) | ||||||
|         assert exc_info.value.detail == ['"2" is not a valid choice.'] |         assert exc_info.value.detail == ['"2" is not a valid choice.'] | ||||||
| 
 | 
 | ||||||
|  |     def test_integer_choices(self): | ||||||
|  |         class ChoiceCase(IntegerChoices): | ||||||
|  |             first = auto() | ||||||
|  |             second = auto() | ||||||
|  |         # Enum validate | ||||||
|  |         choices = [ | ||||||
|  |             (ChoiceCase.first, "1"), | ||||||
|  |             (ChoiceCase.second, "2") | ||||||
|  |         ] | ||||||
|  | 
 | ||||||
|  |         field = serializers.ChoiceField(choices=choices) | ||||||
|  |         assert field.run_validation(1) == 1 | ||||||
|  |         assert field.run_validation(ChoiceCase.first) == 1 | ||||||
|  |         assert field.run_validation("1") == 1 | ||||||
|  | 
 | ||||||
|  |         choices = [ | ||||||
|  |             (ChoiceCase.first.value, "1"), | ||||||
|  |             (ChoiceCase.second.value, "2") | ||||||
|  |         ] | ||||||
|  | 
 | ||||||
|  |         field = serializers.ChoiceField(choices=choices) | ||||||
|  |         assert field.run_validation(1) == 1 | ||||||
|  |         assert field.run_validation(ChoiceCase.first) == 1 | ||||||
|  |         assert field.run_validation("1") == 1 | ||||||
|  | 
 | ||||||
|  |     def test_text_choices(self): | ||||||
|  |         class ChoiceCase(TextChoices): | ||||||
|  |             first = auto() | ||||||
|  |             second = auto() | ||||||
|  |         # Enum validate | ||||||
|  |         choices = [ | ||||||
|  |             (ChoiceCase.first, "first"), | ||||||
|  |             (ChoiceCase.second, "second") | ||||||
|  |         ] | ||||||
|  | 
 | ||||||
|  |         field = serializers.ChoiceField(choices=choices) | ||||||
|  |         assert field.run_validation(ChoiceCase.first) == "first" | ||||||
|  |         assert field.run_validation("first") == "first" | ||||||
|  | 
 | ||||||
|  |         choices = [ | ||||||
|  |             (ChoiceCase.first.value, "first"), | ||||||
|  |             (ChoiceCase.second.value, "second") | ||||||
|  |         ] | ||||||
|  | 
 | ||||||
|  |         field = serializers.ChoiceField(choices=choices) | ||||||
|  |         assert field.run_validation(ChoiceCase.first) == "first" | ||||||
|  |         assert field.run_validation("first") == "first" | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| class TestChoiceFieldWithType(FieldValues): | class TestChoiceFieldWithType(FieldValues): | ||||||
|     """ |     """ | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue
	
	Block a user