From 4994a4b27d581469acc7bec84a92274eee20e788 Mon Sep 17 00:00:00 2001 From: "T. Franzel" Date: Sat, 18 Nov 2023 16:26:29 +0100 Subject: [PATCH] bugfix ignored OpenApiRequest case #1106 --- drf_spectacular/openapi.py | 59 ++++++++++++++++++------------------- drf_spectacular/settings.py | 3 +- tests/test_regressions.py | 13 ++++++++ 3 files changed, 44 insertions(+), 31 deletions(-) diff --git a/drf_spectacular/openapi.py b/drf_spectacular/openapi.py index 04c78b7c..952e45d5 100644 --- a/drf_spectacular/openapi.py +++ b/drf_spectacular/openapi.py @@ -1,5 +1,6 @@ import copy import functools +import itertools import re import typing from collections import defaultdict @@ -1252,42 +1253,40 @@ def _get_request_body(self, direction='request'): return None request_serializer = self.get_request_serializer() + request_body_required = True + content = [] + # either implicit media-types via available parsers or manual list via decoration if isinstance(request_serializer, dict): - content = [] - request_body_required = True - for media_type, serializer in request_serializer.items(): - if isinstance(serializer, OpenApiRequest): - serializer, examples, encoding = ( - serializer.request, serializer.examples, serializer.encoding - ) - else: - encoding, examples = None, None + media_types_iter = request_serializer.items() + else: + media_types_iter = zip(self.map_parsers(), itertools.repeat(request_serializer)) - if ( - encoding - and media_type != "application/x-www-form-urlencoded" - and not media_type.startswith('multipart') - ): - warn( - 'Encodings object on media types other than "application/x-www-form-urlencoded" ' - 'or "multipart/*" have undefined behavior.' - ) + for media_type, serializer in media_types_iter: + if isinstance(serializer, OpenApiRequest): + serializer, examples, encoding = serializer.request, serializer.examples, serializer.encoding + else: + encoding, examples = None, None - schema, partial_request_body_required = self._get_request_for_media_type(serializer, direction) - examples = self._get_examples(serializer, direction, media_type, None, examples) - if schema is None: - continue + if ( + encoding + and media_type != "application/x-www-form-urlencoded" + and not media_type.startswith('multipart') + ): + warn( + 'Encodings object on media types other than "application/x-www-form-urlencoded" ' + 'or "multipart/*" have undefined behavior.' + ) + + examples = self._get_examples(serializer, direction, media_type, None, examples) + schema, partial_request_body_required = self._get_request_for_media_type(serializer, direction) + + if schema is not None: content.append((media_type, schema, examples, encoding)) request_body_required &= partial_request_body_required - else: - schema, request_body_required = self._get_request_for_media_type(request_serializer, direction) - if schema is None: - return None - content = [ - (media_type, schema, self._get_examples(request_serializer, direction, media_type), None) - for media_type in self.map_parsers() - ] + + if not content: + return None request_body = { 'content': { diff --git a/drf_spectacular/settings.py b/drf_spectacular/settings.py index a8e77f28..69c4d9d8 100644 --- a/drf_spectacular/settings.py +++ b/drf_spectacular/settings.py @@ -83,7 +83,8 @@ 'APPEND_PATHS': {}, 'APPEND_COMPONENTS': {}, - # DISCOURAGED - please don't use this anymore as it has tricky implications that + # STRONGLY DISCOURAGED (with the exception for the djangorestframework-api-key library) + # please don't use this anymore as it has tricky implications that # are hard to get right. For authentication, OpenApiAuthenticationExtension are # strongly preferred because they are more robust and easy to write. # However if used, the list of methods is appended to every endpoint in the schema! diff --git a/tests/test_regressions.py b/tests/test_regressions.py index 30dd7a33..b3a68617 100644 --- a/tests/test_regressions.py +++ b/tests/test_regressions.py @@ -3226,3 +3226,16 @@ class X2ViewSet(viewsets.ReadOnlyModelViewSet): 'type': 'array', 'items': {'$ref': '#/components/schemas/Simple'} } + + +def test_openapirequest_used_without_media_type_dict(no_warnings): + @extend_schema(request=OpenApiRequest(SimpleSerializer), responses=None) + @api_view(['POST']) + def view_func(request, format=None): + pass # pragma: no cover + + schema = generate_schema('/x/', view_function=view_func) + + assert get_request_schema(schema['paths']['/x/']['post']) == { + '$ref': '#/components/schemas/Simple' + }