From 324242bf4eac07c61123299729c5b8c2a1914d13 Mon Sep 17 00:00:00 2001 From: Dima Knivets Date: Sun, 11 Aug 2019 15:39:35 +0300 Subject: [PATCH] Schemas: Map renderers/parsers for request/response media-types. --- rest_framework/schemas/openapi.py | 34 ++++++++++++++++++++++++++----- tests/schemas/test_openapi.py | 24 ++++++++++++++++++++++ 2 files changed, 53 insertions(+), 5 deletions(-) diff --git a/rest_framework/schemas/openapi.py b/rest_framework/schemas/openapi.py index 4fee95439..cd70b9d0c 100644 --- a/rest_framework/schemas/openapi.py +++ b/rest_framework/schemas/openapi.py @@ -1,4 +1,5 @@ import warnings +from operator import attrgetter from urllib.parse import urljoin from django.core.validators import ( @@ -8,7 +9,7 @@ from django.core.validators import ( from django.db import models from django.utils.encoding import force_str -from rest_framework import exceptions, serializers +from rest_framework import exceptions, renderers, serializers from rest_framework.compat import uritemplate from rest_framework.fields import _UnvalidatedField, empty @@ -78,7 +79,9 @@ class SchemaGenerator(BaseSchemaGenerator): class AutoSchema(ViewInspector): - content_types = ['application/json'] + request_media_types = [] + response_media_types = [] + method_mapping = { 'get': 'Retrieve', 'post': 'Create', @@ -336,6 +339,12 @@ class AutoSchema(ViewInspector): self._map_min_max(field, content) return content + if isinstance(field, serializers.FileField): + return { + 'type': 'string', + 'format': 'binary' + } + # Simplest cases, default to 'string' type: FIELD_CLASS_SCHEMA_TYPE = { serializers.BooleanField: 'boolean', @@ -430,9 +439,20 @@ class AutoSchema(ViewInspector): pagination_class = getattr(self.view, 'pagination_class', None) if pagination_class: return pagination_class() - return None + def map_parsers(self, path, method): + return list(map(attrgetter('media_type'), self.view.parser_classes)) + + def map_renderers(self, path, method): + media_types = [] + for renderer in self.view.renderer_classes: + # BrowsableAPIRenderer not relevant to OpenAPI spec + if renderer == renderers.BrowsableAPIRenderer: + continue + media_types.append(renderer.media_type) + return media_types + def _get_serializer(self, method, path): view = self.view @@ -452,6 +472,8 @@ class AutoSchema(ViewInspector): if method not in ('PUT', 'PATCH', 'POST'): return {} + self.request_media_types = self.map_parsers(path, method) + serializer = self._get_serializer(path, method) if not isinstance(serializer, serializers.Serializer): @@ -469,7 +491,7 @@ class AutoSchema(ViewInspector): return { 'content': { ct: {'schema': content} - for ct in self.content_types + for ct in self.request_media_types } } @@ -482,6 +504,8 @@ class AutoSchema(ViewInspector): } } + self.response_media_types = self.map_renderers(path, method) + item_schema = {} serializer = self._get_serializer(path, method) @@ -509,7 +533,7 @@ class AutoSchema(ViewInspector): '200': { 'content': { ct: {'schema': response_schema} - for ct in self.content_types + for ct in self.response_media_types }, # description is a mandatory property, # https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#responseObject diff --git a/tests/schemas/test_openapi.py b/tests/schemas/test_openapi.py index 508e7dba8..6c9089e05 100644 --- a/tests/schemas/test_openapi.py +++ b/tests/schemas/test_openapi.py @@ -339,6 +339,30 @@ class TestOperationIntrospection(TestCase): }, } + def test_multipart_request_body_generation(self): + """Test that a view's delete method generates a proper response body schema.""" + path = '/{id}/' + method = 'POST' + + class ItemSerializer(serializers.Serializer): + attachment = serializers.FileField() + + class View(generics.CreateAPIView): + serializer_class = ItemSerializer + + view = create_view( + View, + method, + create_request(path), + ) + inspector = AutoSchema() + inspector.view = view + + request_body = inspector._get_request_body(path, method) + assert 'multipart/form-data' in request_body['content'] + attachment = request_body['content']['multipart/form-data']['schema']['properties']['attachment'] + assert attachment['format'] == 'binary' + def test_retrieve_response_body_generation(self): """ Test that a list of properties is returned for retrieve item views.