From 2832c2ed2ee18d2da429d0b1db03f2f6573fbbfb Mon Sep 17 00:00:00 2001 From: G-Glitch404 Date: Wed, 7 Oct 2026 20:39:04 +0300 Subject: [PATCH 1/2] Fix custom ChoiceField mappings for model choices --- rest_framework/serializers.py | 7 ++++--- tests/test_model_serializer.py | 28 ++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index adb6a65b5b..d919cc0022 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -1301,9 +1301,10 @@ def build_standard_field(self, field_name, model_field): field_kwargs['queryset'] = model_field.related_model.objects if 'choices' in field_kwargs: - # Fields with choices get coerced into `ChoiceField` - # instead of using their regular typed field. - field_class = self.serializer_choice_field + # Fields with choices default to `ChoiceField`, unless a custom + # `ChoiceField` subclass is already mapped for the model field. + if not issubclass(field_class, self.serializer_choice_field): + field_class = self.serializer_choice_field # Some model fields may introduce kwargs that would not be valid # for the choice field. We need to strip these out. # Eg. models.DecimalField(max_digits=3, decimal_places=1, choices=DECIMAL_CHOICES) diff --git a/tests/test_model_serializer.py b/tests/test_model_serializer.py index 5ad9cc264c..decd22a3fb 100644 --- a/tests/test_model_serializer.py +++ b/tests/test_model_serializer.py @@ -369,6 +369,34 @@ class Meta: assert choices_field_with_nonstandard_args.choices assert choices_field_with_nonstandard_args.read_only is False + def test_custom_choice_field_mapping(self): + class CustomModelField(models.CharField): + pass + + class CustomChoiceField(serializers.ChoiceField): + pass + + class ChoiceModel(models.Model): + field = CustomModelField( + max_length=10, + choices=COLOR_CHOICES, + ) + + class TestSerializer(serializers.ModelSerializer): + serializer_field_mapping = { + **serializers.ModelSerializer.serializer_field_mapping, + CustomModelField: CustomChoiceField, + } + + class Meta: + model = ChoiceModel + fields = ('field',) + + field = TestSerializer().fields['field'] + + assert isinstance(field, CustomChoiceField) + assert list(field.choices) == [choice[0] for choice in COLOR_CHOICES] + def test_non_editable_choice_field(self): class ExampleSerializer(serializers.ModelSerializer): class Meta: From 3de78e3305471994556d502dad6b917a9d891af9 Mon Sep 17 00:00:00 2001 From: G-Glitch404 Date: Wed, 7 Oct 2026 21:04:50 +0300 Subject: [PATCH 2/2] Avoid test model name collision --- tests/test_model_serializer.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_model_serializer.py b/tests/test_model_serializer.py index decd22a3fb..077e981578 100644 --- a/tests/test_model_serializer.py +++ b/tests/test_model_serializer.py @@ -376,7 +376,7 @@ class CustomModelField(models.CharField): class CustomChoiceField(serializers.ChoiceField): pass - class ChoiceModel(models.Model): + class CustomChoiceModel(models.Model): field = CustomModelField( max_length=10, choices=COLOR_CHOICES, @@ -389,7 +389,7 @@ class TestSerializer(serializers.ModelSerializer): } class Meta: - model = ChoiceModel + model = CustomChoiceModel fields = ('field',) field = TestSerializer().fields['field']