diff --git a/drf_spectacular/openapi.py b/drf_spectacular/openapi.py index 21f44082..7c89c27a 100644 --- a/drf_spectacular/openapi.py +++ b/drf_spectacular/openapi.py @@ -481,9 +481,10 @@ def _map_serializer_field(self, field, direction): model_field = field.queryset.model._meta.pk else: if isinstance(field.parent, serializers.ManyRelatedField): - model_field = field.parent.parent.Meta.model._meta.pk + relation_field = field.parent.parent.Meta.model._meta.get_field(field.parent.source) else: - model_field = field.parent.Meta.model._meta.pk + relation_field = field.parent.Meta.model._meta.get_field(field.source) + model_field = relation_field.related_model._meta.pk # primary keys are usually non-editable (readOnly=True) and map_model_field correctly # signals that attribute. however this does not apply in the context of relations. diff --git a/tests/test_regressions.py b/tests/test_regressions.py index 8817ffb2..b879d406 100644 --- a/tests/test_regressions.py +++ b/tests/test_regressions.py @@ -1314,3 +1314,36 @@ def view_func(request, format=None): assert 'explode' in parameter assert 'style' in parameter assert parameter['schema']['type'] == 'array' + + +def test_incorrect_foreignkey_type_on_readonly_field(no_warnings): + class ReferencingModel(models.Model): + id = models.UUIDField(primary_key=True) + referenced_model = models.ForeignKey(SimpleModel, on_delete=models.CASCADE) + referenced_model_ro = models.ForeignKey(SimpleModel, on_delete=models.CASCADE) + referenced_model_m2m = models.ManyToManyField(SimpleModel) + referenced_model_m2m_ro = models.ManyToManyField(SimpleModel) + + class ReferencingModelSerializer(serializers.ModelSerializer): + indirect_referenced_model_ro = serializers.PrimaryKeyRelatedField( + source='referenced_model', + read_only=True, + ) + + class Meta: + fields = '__all__' + read_only_fields = ['id', 'referenced_model_ro', 'referenced_model_m2m_ro'] + model = ReferencingModel + + class ReferencingModelViewset(viewsets.ModelViewSet): + serializer_class = ReferencingModelSerializer + queryset = ReferencingModel.objects.all() + + schema = generate_schema('/x/', ReferencingModelViewset) + properties = schema['components']['schemas']['ReferencingModel']['properties'] + + assert properties['referenced_model']['type'] == 'integer' + assert properties['referenced_model_ro']['type'] == 'integer' + assert properties['referenced_model_m2m']['items']['type'] == 'integer' + assert properties['referenced_model_m2m_ro']['items']['type'] == 'integer' + assert properties['indirect_referenced_model_ro']['type'] == 'integer'