diff --git a/rest_framework/schemas/openapi.py b/rest_framework/schemas/openapi.py index 4fee95439b..cd70b9d0c2 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.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 @@ def get_schema(self, request=None, public=False): class AutoSchema(ViewInspector): - content_types = ['application/json'] + request_media_types = [] + response_media_types = [] + method_mapping = { 'get': 'Retrieve', 'post': 'Create', @@ -336,6 +339,12 @@ def _map_field(self, field): 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 @@ def _get_pagninator(self): 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 @@ def _get_request_body(self, path, method): 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 @@ def _get_request_body(self, path, method): return { 'content': { ct: {'schema': content} - for ct in self.content_types + for ct in self.request_media_types } } @@ -482,6 +504,8 @@ def _get_responses(self, path, method): } } + self.response_media_types = self.map_renderers(path, method) + item_schema = {} serializer = self._get_serializer(path, method) @@ -509,7 +533,7 @@ def _get_responses(self, path, method): '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 508e7dba8f..9197b4478d 100644 --- a/tests/schemas/test_openapi.py +++ b/tests/schemas/test_openapi.py @@ -5,6 +5,8 @@ from rest_framework import filters, generics, pagination, routers, serializers from rest_framework.compat import uritemplate +from rest_framework.parsers import JSONParser, MultiPartParser +from rest_framework.renderers import JSONRenderer from rest_framework.request import Request from rest_framework.schemas.openapi import AutoSchema, SchemaGenerator @@ -339,6 +341,77 @@ class View(generics.DestroyAPIView): }, } + def test_parser_mapping(self): + """Test that view's parsers are mapped to OA media types""" + path = '/{id}/' + method = 'POST' + + class View(generics.CreateAPIView): + serializer_class = views.ExampleSerializer + parser_classes = [JSONParser, MultiPartParser] + + view = create_view( + View, + method, + create_request(path), + ) + inspector = AutoSchema() + inspector.view = view + + request_body = inspector._get_request_body(path, method) + + assert len(request_body['content'].keys()) == 2 + assert 'multipart/form-data' in request_body['content'] + assert 'application/json' in request_body['content'] + + def test_renderer_mapping(self): + """Test that view's renderers are mapped to OA media types""" + path = '/{id}/' + method = 'GET' + + class View(generics.CreateAPIView): + serializer_class = views.ExampleSerializer + renderer_classes = [JSONRenderer] + + view = create_view( + View, + method, + create_request(path), + ) + inspector = AutoSchema() + inspector.view = view + + responses = inspector._get_responses(path, method) + # TODO this should be changed once the multiple response + # schema support is there + success_response = responses['200'] + + assert len(success_response['content'].keys()) == 1 + assert 'application/json' in success_response['content'] + + def test_serializer_filefield(self): + 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) + mp_media = request_body['content']['multipart/form-data'] + attachment = mp_media['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. pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy