From e5b7361e3ab38df13dc112c627317edeaf1b9c64 Mon Sep 17 00:00:00 2001 From: "T. Franzel" Date: Tue, 7 May 2024 10:38:20 +0200 Subject: [PATCH] add choice field display method handling #1228 --- drf_spectacular/openapi.py | 9 ++++++++- tests/test_regressions.py | 25 +++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/drf_spectacular/openapi.py b/drf_spectacular/openapi.py index d954914b..f4f14f3b 100644 --- a/drf_spectacular/openapi.py +++ b/drf_spectacular/openapi.py @@ -893,7 +893,14 @@ def _map_serializer_field(self, field, direction, bypass_extensions=False): return append_meta(build_basic_type(OpenApiTypes.STR), meta) target = follow_field_source(model, source) - if callable(target): + if ( + hasattr(target, "_partialmethod") + and target._partialmethod.func.__name__ == '_get_FIELD_display' + ): + # the only way to detect an uninitialized partial method + # this is a convenience method for model choice fields and is mostly a string + schema = build_basic_type(str) + elif callable(target): schema = self._map_response_type_hint(target) elif isinstance(target, models.Field): schema = self._map_model_field(target, direction) diff --git a/tests/test_regressions.py b/tests/test_regressions.py index 2cfeecaf..3fe0bf76 100644 --- a/tests/test_regressions.py +++ b/tests/test_regressions.py @@ -3385,3 +3385,28 @@ def view_func(request, format=None): 'baz': {'type': 'integer'}, 'qux': {'items': {'type': 'integer'}, 'type': 'array'} } + + +def test_model_choice_display_method_on_readonly(no_warnings): + class M15(models.Model): + SHIRT_SIZES = {"S": "Small", "M": "Medium", "L": "Large"} + + name = models.CharField(max_length=60) + shirt_size = models.CharField(max_length=1, choices=SHIRT_SIZES) + + class XSerializer(serializers.ModelSerializer): + field_name = serializers.ReadOnlyField(source='name') + field_shirts = serializers.ReadOnlyField(source='get_shirt_size_display') + + class Meta: + model = M15 + fields = '__all__' + + class XViewset(viewsets.ModelViewSet): + queryset = M15.objects.all() + serializer_class = XSerializer + + schema = generate_schema('x', XViewset) + assert schema['components']['schemas']['X']["properties"]["field_shirts"] == { + "readOnly": True, 'type': 'string' + }