Skip to content

Commit 8b06ce7

Browse files
knivetscarltongibson
authored andcommitted
OpenAPI: Map renderers/parsers for request/response media-types. (#6865)
1 parent 14d740d commit 8b06ce7

File tree

2 files changed

+102
-5
lines changed

2 files changed

+102
-5
lines changed

rest_framework/schemas/openapi.py

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import warnings
2+
from operator import attrgetter
23
from urllib.parse import urljoin
34

45
from django.core.validators import (
@@ -8,7 +9,7 @@
89
from django.db import models
910
from django.utils.encoding import force_str
1011

11-
from rest_framework import exceptions, serializers
12+
from rest_framework import exceptions, renderers, serializers
1213
from rest_framework.compat import uritemplate
1314
from rest_framework.fields import _UnvalidatedField, empty
1415

@@ -78,7 +79,9 @@ def get_schema(self, request=None, public=False):
7879

7980
class AutoSchema(ViewInspector):
8081

81-
content_types = ['application/json']
82+
request_media_types = []
83+
response_media_types = []
84+
8285
method_mapping = {
8386
'get': 'Retrieve',
8487
'post': 'Create',
@@ -339,6 +342,12 @@ def _map_field(self, field):
339342
self._map_min_max(field, content)
340343
return content
341344

345+
if isinstance(field, serializers.FileField):
346+
return {
347+
'type': 'string',
348+
'format': 'binary'
349+
}
350+
342351
# Simplest cases, default to 'string' type:
343352
FIELD_CLASS_SCHEMA_TYPE = {
344353
serializers.BooleanField: 'boolean',
@@ -434,9 +443,20 @@ def _get_paginator(self):
434443
pagination_class = getattr(self.view, 'pagination_class', None)
435444
if pagination_class:
436445
return pagination_class()
437-
438446
return None
439447

448+
def map_parsers(self, path, method):
449+
return list(map(attrgetter('media_type'), self.view.parser_classes))
450+
451+
def map_renderers(self, path, method):
452+
media_types = []
453+
for renderer in self.view.renderer_classes:
454+
# BrowsableAPIRenderer not relevant to OpenAPI spec
455+
if renderer == renderers.BrowsableAPIRenderer:
456+
continue
457+
media_types.append(renderer.media_type)
458+
return media_types
459+
440460
def _get_serializer(self, method, path):
441461
view = self.view
442462

@@ -456,6 +476,8 @@ def _get_request_body(self, path, method):
456476
if method not in ('PUT', 'PATCH', 'POST'):
457477
return {}
458478

479+
self.request_media_types = self.map_parsers(path, method)
480+
459481
serializer = self._get_serializer(path, method)
460482

461483
if not isinstance(serializer, serializers.Serializer):
@@ -473,7 +495,7 @@ def _get_request_body(self, path, method):
473495
return {
474496
'content': {
475497
ct: {'schema': content}
476-
for ct in self.content_types
498+
for ct in self.request_media_types
477499
}
478500
}
479501

@@ -486,6 +508,8 @@ def _get_responses(self, path, method):
486508
}
487509
}
488510

511+
self.response_media_types = self.map_renderers(path, method)
512+
489513
item_schema = {}
490514
serializer = self._get_serializer(path, method)
491515

@@ -513,7 +537,7 @@ def _get_responses(self, path, method):
513537
'200': {
514538
'content': {
515539
ct: {'schema': response_schema}
516-
for ct in self.content_types
540+
for ct in self.response_media_types
517541
},
518542
# description is a mandatory property,
519543
# https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#responseObject

tests/schemas/test_openapi.py

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55

66
from rest_framework import filters, generics, pagination, routers, serializers
77
from rest_framework.compat import uritemplate
8+
from rest_framework.parsers import JSONParser, MultiPartParser
9+
from rest_framework.renderers import JSONRenderer
810
from rest_framework.request import Request
911
from rest_framework.schemas.openapi import AutoSchema, SchemaGenerator
1012

@@ -364,6 +366,77 @@ class View(generics.DestroyAPIView):
364366
},
365367
}
366368

369+
def test_parser_mapping(self):
370+
"""Test that view's parsers are mapped to OA media types"""
371+
path = '/{id}/'
372+
method = 'POST'
373+
374+
class View(generics.CreateAPIView):
375+
serializer_class = views.ExampleSerializer
376+
parser_classes = [JSONParser, MultiPartParser]
377+
378+
view = create_view(
379+
View,
380+
method,
381+
create_request(path),
382+
)
383+
inspector = AutoSchema()
384+
inspector.view = view
385+
386+
request_body = inspector._get_request_body(path, method)
387+
388+
assert len(request_body['content'].keys()) == 2
389+
assert 'multipart/form-data' in request_body['content']
390+
assert 'application/json' in request_body['content']
391+
392+
def test_renderer_mapping(self):
393+
"""Test that view's renderers are mapped to OA media types"""
394+
path = '/{id}/'
395+
method = 'GET'
396+
397+
class View(generics.CreateAPIView):
398+
serializer_class = views.ExampleSerializer
399+
renderer_classes = [JSONRenderer]
400+
401+
view = create_view(
402+
View,
403+
method,
404+
create_request(path),
405+
)
406+
inspector = AutoSchema()
407+
inspector.view = view
408+
409+
responses = inspector._get_responses(path, method)
410+
# TODO this should be changed once the multiple response
411+
# schema support is there
412+
success_response = responses['200']
413+
414+
assert len(success_response['content'].keys()) == 1
415+
assert 'application/json' in success_response['content']
416+
417+
def test_serializer_filefield(self):
418+
path = '/{id}/'
419+
method = 'POST'
420+
421+
class ItemSerializer(serializers.Serializer):
422+
attachment = serializers.FileField()
423+
424+
class View(generics.CreateAPIView):
425+
serializer_class = ItemSerializer
426+
427+
view = create_view(
428+
View,
429+
method,
430+
create_request(path),
431+
)
432+
inspector = AutoSchema()
433+
inspector.view = view
434+
435+
request_body = inspector._get_request_body(path, method)
436+
mp_media = request_body['content']['multipart/form-data']
437+
attachment = mp_media['schema']['properties']['attachment']
438+
assert attachment['format'] == 'binary'
439+
367440
def test_retrieve_response_body_generation(self):
368441
"""
369442
Test that a list of properties is returned for retrieve item views.

0 commit comments

Comments
 (0)
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