Allow overriding field mappings for choice field

This commit is contained in:
Yuekui Li 2021-07-13 20:48:28 -07:00
parent 98e56e0327
commit 1f9f1ce6a2
3 changed files with 28 additions and 11 deletions

View File

@ -1412,13 +1412,12 @@ class ChoiceField(Field):
html_cutoff = None html_cutoff = None
html_cutoff_text = _('More than {count} items...') html_cutoff_text = _('More than {count} items...')
def __init__(self, choices, **kwargs): def __init__(self, choices, allow_blank=False, **kwargs):
self.choices = choices self.choices = choices
self.allow_blank = allow_blank
self.html_cutoff = kwargs.pop('html_cutoff', self.html_cutoff) self.html_cutoff = kwargs.pop('html_cutoff', self.html_cutoff)
self.html_cutoff_text = kwargs.pop('html_cutoff_text', self.html_cutoff_text) self.html_cutoff_text = kwargs.pop('html_cutoff_text', self.html_cutoff_text)
self.allow_blank = kwargs.pop('allow_blank', False)
super().__init__(**kwargs) super().__init__(**kwargs)
def to_internal_value(self, data): def to_internal_value(self, data):

View File

@ -1212,20 +1212,19 @@ class ModelSerializer(Serializer):
field_class = self.serializer_related_field field_class = self.serializer_related_field
field_kwargs['queryset'] = model_field.related_model.objects field_kwargs['queryset'] = model_field.related_model.objects
if 'choices' in field_kwargs: if 'choices' in field_kwargs and not issubclass(field_class, self.serializer_choice_field):
# Fields with choices get coerced into `ChoiceField` # Fields with choices get coerced into `ChoiceField`
# instead of using their regular typed field. # instead of using their regular typed field.
field_class = self.serializer_choice_field field_class = self.serializer_choice_field
# Some model fields may introduce kwargs that would not be valid # Some model fields may introduce kwargs that would not be valid
# for the choice field. We need to strip these out. # for the choice field. We need to strip these out.
# Eg. models.DecimalField(max_digits=3, decimal_places=1, choices=DECIMAL_CHOICES) # Eg. models.DecimalField(max_digits=3, decimal_places=1, choices=DECIMAL_CHOICES)
valid_kwargs = { valid_kwargs = set()
'read_only', 'write_only', for c in inspect.getmro(field_class):
'required', 'default', 'initial', 'source', sig = inspect.signature(c.__init__)
'label', 'help_text', 'style', for param in sig.parameters.values():
'error_messages', 'validators', 'allow_null', 'allow_blank', if (param.kind == param.POSITIONAL_OR_KEYWORD):
'choices' valid_kwargs.add(param.name)
}
for key in list(field_kwargs): for key in list(field_kwargs):
if key not in valid_kwargs: if key not in valid_kwargs:
field_kwargs.pop(key) field_kwargs.pop(key)

View File

@ -217,6 +217,25 @@ class TestRegularFieldMappings(TestCase):
""") """)
self.assertEqual(repr(TestSerializer()), expected) self.assertEqual(repr(TestSerializer()), expected)
def test_override_choice_field_mapping(self):
class CustomChoiceField(models.CharField):
"""
A custom choice model field simply for testing purposes.
"""
max_length = 100
class CostomizedChoiceModel(models.Model):
choices_field = CustomChoiceField(choices=COLOR_CHOICES)
class TestSerializer(serializers.ModelSerializer):
class Meta:
model = CostomizedChoiceModel
fields = '__all__'
self.assertTrue(isinstance(TestSerializer().fields["choices_field"], serializers.ChoiceField))
TestSerializer.serializer_field_mapping[CustomChoiceField] = serializers.MultipleChoiceField
self.assertTrue(isinstance(TestSerializer().fields["choices_field"], serializers.MultipleChoiceField))
def test_nullable_boolean_field_choices(self): def test_nullable_boolean_field_choices(self):
class NullableBooleanChoicesModel(models.Model): class NullableBooleanChoicesModel(models.Model):
CHECKLIST_OPTIONS = ( CHECKLIST_OPTIONS = (