Fix schema generation of ManyRelatedField to detect the child type (#6489)

* Introspect ManyRelatedField data type recursively

For all `ManyRelatedField` objects, we were assuming that the inner type was always a `String`. While this may be true for the default output, a `ManyRelatedField` is a wrapper for a lot of other classes which includes more than just strings. This should allow us to document lists of things other than strings.

* Added test for schemas for many-to-many fields

This adds a test that makes sure we generate the schema for a many-to-many field such that it actually has the right type. For some reason we did not previously have any tests for schema generation that included them, so hopefully this will prevent any future issues from popping up.

This should serve as a regression test for the `items` field on to-many relationships, which was previously forced to a `String` even though in most cases it is a different inner type within the array.
This commit is contained in:
Kevin Brown 2019-03-07 05:26:03 -05:00 committed by Carlton Gibson
parent fd32dd7ca4
commit 86c72bb226
2 changed files with 49 additions and 2 deletions

View File

@ -51,8 +51,10 @@ def field_to_schema(field):
description=description
)
elif isinstance(field, serializers.ManyRelatedField):
related_field_schema = field_to_schema(field.child_relation)
return coreschema.Array(
items=coreschema.String(),
items=related_field_schema,
title=title,
description=description
)

View File

@ -24,7 +24,7 @@ from rest_framework.utils import formatting
from rest_framework.views import APIView
from rest_framework.viewsets import GenericViewSet, ModelViewSet
from .models import BasicModel, ForeignKeySource
from .models import BasicModel, ForeignKeySource, ManyToManySource
factory = APIRequestFactory()
@ -701,6 +701,51 @@ class TestSchemaGeneratorWithForeignKey(TestCase):
assert schema == expected
class ManyToManySourceSerializer(serializers.ModelSerializer):
class Meta:
model = ManyToManySource
fields = ('id', 'name', 'targets')
class ManyToManySourceView(generics.CreateAPIView):
queryset = ManyToManySource.objects.all()
serializer_class = ManyToManySourceSerializer
@unittest.skipUnless(coreapi, 'coreapi is not installed')
class TestSchemaGeneratorWithManyToMany(TestCase):
def setUp(self):
self.patterns = [
url(r'^example/?$', ManyToManySourceView.as_view()),
]
def test_schema_for_regular_views(self):
"""
Ensure that AutoField many to many fields are output as Integer.
"""
generator = SchemaGenerator(title='Example API', patterns=self.patterns)
schema = generator.get_schema()
expected = coreapi.Document(
url='',
title='Example API',
content={
'example': {
'create': coreapi.Link(
url='/example/',
action='post',
encoding='application/json',
fields=[
coreapi.Field('name', required=True, location='form', schema=coreschema.String(title='Name')),
coreapi.Field('targets', required=True, location='form', schema=coreschema.Array(title='Targets', items=coreschema.Integer())),
]
)
}
}
)
assert schema == expected
@unittest.skipUnless(coreapi, 'coreapi is not installed')
class Test4605Regression(TestCase):
def test_4605_regression(self):