Skip to content

Commit 324242b

Browse files
knivetscarltongibson
authored andcommitted
Schemas: Map renderers/parsers for request/response media-types.
1 parent 1cc4be4 commit 324242b

File tree

2 files changed

+53
-5
lines changed

2 files changed

+53
-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',
@@ -336,6 +339,12 @@ def _map_field(self, field):
336339
self._map_min_max(field, content)
337340
return content
338341

342+
if isinstance(field, serializers.FileField):
343+
return {
344+
'type': 'string',
345+
'format': 'binary'
346+
}
347+
339348
# Simplest cases, default to 'string' type:
340349
FIELD_CLASS_SCHEMA_TYPE = {
341350
serializers.BooleanField: 'boolean',
@@ -430,9 +439,20 @@ def _get_pagninator(self):
430439
pagination_class = getattr(self.view, 'pagination_class', None)
431440
if pagination_class:
432441
return pagination_class()
433-
434442
return None
435443

444+
def map_parsers(self, path, method):
445+
return list(map(attrgetter('media_type'), self.view.parser_classes))
446+
447+
def map_renderers(self, path, method):
448+
media_types = []
449+
for renderer in self.view.renderer_classes:
450+
# BrowsableAPIRenderer not relevant to OpenAPI spec
451+
if renderer == renderers.BrowsableAPIRenderer:
452+
continue
453+
media_types.append(renderer.media_type)
454+
return media_types
455+
436456
def _get_serializer(self, method, path):
437457
view = self.view
438458

@@ -452,6 +472,8 @@ def _get_request_body(self, path, method):
452472
if method not in ('PUT', 'PATCH', 'POST'):
453473
return {}
454474

475+
self.request_media_types = self.map_parsers(path, method)
476+
455477
serializer = self._get_serializer(path, method)
456478

457479
if not isinstance(serializer, serializers.Serializer):
@@ -469,7 +491,7 @@ def _get_request_body(self, path, method):
469491
return {
470492
'content': {
471493
ct: {'schema': content}
472-
for ct in self.content_types
494+
for ct in self.request_media_types
473495
}
474496
}
475497

@@ -482,6 +504,8 @@ def _get_responses(self, path, method):
482504
}
483505
}
484506

507+
self.response_media_types = self.map_renderers(path, method)
508+
485509
item_schema = {}
486510
serializer = self._get_serializer(path, method)
487511

@@ -509,7 +533,7 @@ def _get_responses(self, path, method):
509533
'200': {
510534
'content': {
511535
ct: {'schema': response_schema}
512-
for ct in self.content_types
536+
for ct in self.response_media_types
513537
},
514538
# description is a mandatory property,
515539
# https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#responseObject

tests/schemas/test_openapi.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,30 @@ class View(generics.DestroyAPIView):
339339
},
340340
}
341341

342+
def test_multipart_request_body_generation(self):
343+
"""Test that a view's delete method generates a proper response body schema."""
344+
path = '/{id}/'
345+
method = 'POST'
346+
347+
class ItemSerializer(serializers.Serializer):
348+
attachment = serializers.FileField()
349+
350+
class View(generics.CreateAPIView):
351+
serializer_class = ItemSerializer
352+
353+
view = create_view(
354+
View,
355+
method,
356+
create_request(path),
357+
)
358+
inspector = AutoSchema()
359+
inspector.view = view
360+
361+
request_body = inspector._get_request_body(path, method)
362+
assert 'multipart/form-data' in request_body['content']
363+
attachment = request_body['content']['multipart/form-data']['schema']['properties']['attachment']
364+
assert attachment['format'] == 'binary'
365+
342366
def test_retrieve_response_body_generation(self):
343367
"""
344368
Test that a list of properties is returned for retrieve item views.

0 commit comments

Comments
 (0)