Skip to content

Commit 9d91a27

Browse files
committed
Added OpenAPI Schema Generation.
1 parent d2d1888 commit 9d91a27

File tree

11 files changed

+738
-227
lines changed

11 files changed

+738
-227
lines changed

rest_framework/filters.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@ def get_schema_fields(self, view):
4040
assert coreschema is not None, 'coreschema must be installed to use `get_schema_fields()`'
4141
return []
4242

43+
def get_schema_operation_parameters(self, view):
44+
return []
45+
4346

4447
class SearchFilter(BaseFilterBackend):
4548
# The URL query parameter used for the search.
@@ -159,6 +162,19 @@ def get_schema_fields(self, view):
159162
)
160163
]
161164

165+
def get_schema_operation_parameters(self, view):
166+
return [
167+
{
168+
'name': self.search_param,
169+
'required': False,
170+
'in': 'query',
171+
'description': force_text(self.search_description),
172+
'schema': {
173+
'type': 'string',
174+
},
175+
},
176+
]
177+
162178

163179
class OrderingFilter(BaseFilterBackend):
164180
# The URL query parameter used for the ordering.
@@ -290,6 +306,19 @@ def get_schema_fields(self, view):
290306
)
291307
]
292308

309+
def get_schema_operation_parameters(self, view):
310+
return [
311+
{
312+
'name': self.ordering_param,
313+
'required': False,
314+
'in': 'query',
315+
'description': force_text(self.ordering_description),
316+
'schema': {
317+
'type': 'string',
318+
},
319+
},
320+
]
321+
293322

294323
class DjangoObjectPermissionsFilter(BaseFilterBackend):
295324
"""
Lines changed: 12 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,32 @@
11
from django.core.management.base import BaseCommand
22

3-
from rest_framework.compat import coreapi
4-
from rest_framework.renderers import (
5-
CoreJSONRenderer, JSONOpenAPIRenderer, OpenAPIRenderer
6-
)
7-
from rest_framework.schemas.generators import SchemaGenerator
3+
from rest_framework.compat import yaml
4+
from rest_framework.schemas.generators import OpenAPISchemaGenerator
5+
from rest_framework.utils import json
86

97

108
class Command(BaseCommand):
119
help = "Generates configured API schema for project."
1210

1311
def add_arguments(self, parser):
14-
parser.add_argument('--title', dest="title", default=None, type=str)
12+
parser.add_argument('--title', dest="title", default='', type=str)
1513
parser.add_argument('--url', dest="url", default=None, type=str)
1614
parser.add_argument('--description', dest="description", default=None, type=str)
17-
parser.add_argument('--format', dest="format", choices=['openapi', 'openapi-json', 'corejson'], default='openapi', type=str)
15+
parser.add_argument('--format', dest="format", choices=['openapi', 'openapi-json'], default='openapi', type=str)
1816

1917
def handle(self, *args, **options):
20-
assert coreapi is not None, 'coreapi must be installed.'
21-
22-
generator = SchemaGenerator(
18+
generator = OpenAPISchemaGenerator(
2319
url=options['url'],
2420
title=options['title'],
2521
description=options['description']
2622
)
2723

2824
schema = generator.get_schema(request=None, public=True)
2925

30-
renderer = self.get_renderer(options['format'])
31-
output = renderer.render(schema, renderer_context={})
32-
self.stdout.write(output.decode('utf-8'))
33-
34-
def get_renderer(self, format):
35-
renderer_cls = {
36-
'corejson': CoreJSONRenderer,
37-
'openapi': OpenAPIRenderer,
38-
'openapi-json': JSONOpenAPIRenderer,
39-
}[format]
26+
# TODO: Handle via renderer? More options?
27+
if options['format'] == 'openapi':
28+
output = yaml.dump(schema, default_flow_style=False)
29+
else:
30+
output = json.dumps(schema, indent=2)
4031

41-
return renderer_cls()
32+
self.stdout.write(output)

rest_framework/pagination.py

Lines changed: 86 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,9 @@ def get_schema_fields(self, view):
152152
assert coreapi is not None, 'coreapi must be installed to use `get_schema_fields()`'
153153
return []
154154

155+
def get_schema_operation_parameters(self, view):
156+
return []
157+
155158

156159
class PageNumberPagination(BasePagination):
157160
"""
@@ -305,6 +308,32 @@ def get_schema_fields(self, view):
305308
)
306309
return fields
307310

311+
def get_schema_operation_parameters(self, view):
312+
parameters = [
313+
{
314+
'name': self.page_query_param,
315+
'required': False,
316+
'in': 'query',
317+
'description': force_text(self.page_query_description),
318+
'schema': {
319+
'type': 'integer',
320+
},
321+
},
322+
]
323+
if self.page_size_query_param is not None:
324+
parameters.append(
325+
{
326+
'name': self.page_size_query_param,
327+
'required': False,
328+
'in': 'query',
329+
'description': force_text(self.page_size_query_description),
330+
'schema': {
331+
'type': 'integer',
332+
},
333+
},
334+
)
335+
return parameters
336+
308337

309338
class LimitOffsetPagination(BasePagination):
310339
"""
@@ -434,6 +463,15 @@ def to_html(self):
434463
context = self.get_html_context()
435464
return template.render(context)
436465

466+
def get_count(self, queryset):
467+
"""
468+
Determine an object count, supporting either querysets or regular lists.
469+
"""
470+
try:
471+
return queryset.count()
472+
except (AttributeError, TypeError):
473+
return len(queryset)
474+
437475
def get_schema_fields(self, view):
438476
assert coreapi is not None, 'coreapi must be installed to use `get_schema_fields()`'
439477
assert coreschema is not None, 'coreschema must be installed to use `get_schema_fields()`'
@@ -458,14 +496,28 @@ def get_schema_fields(self, view):
458496
)
459497
]
460498

461-
def get_count(self, queryset):
462-
"""
463-
Determine an object count, supporting either querysets or regular lists.
464-
"""
465-
try:
466-
return queryset.count()
467-
except (AttributeError, TypeError):
468-
return len(queryset)
499+
def get_schema_operation_parameters(self, view):
500+
parameters = [
501+
{
502+
'name': self.limit_query_param,
503+
'required': False,
504+
'in': 'query',
505+
'description': force_text(self.limit_query_description),
506+
'schema': {
507+
'type': 'integer',
508+
},
509+
},
510+
{
511+
'name': self.offset_query_param,
512+
'required': False,
513+
'in': 'query',
514+
'description': force_text(self.offset_query_description),
515+
'schema': {
516+
'type': 'integer',
517+
},
518+
},
519+
]
520+
return parameters
469521

470522

471523
class CursorPagination(BasePagination):
@@ -820,3 +872,29 @@ def get_schema_fields(self, view):
820872
)
821873
)
822874
return fields
875+
876+
def get_schema_operation_parameters(self, view):
877+
parameters = [
878+
{
879+
'name': self.cursor_query_param,
880+
'required': False,
881+
'in': 'query',
882+
'description': force_text(self.cursor_query_description),
883+
'schema': {
884+
'type': 'integer',
885+
},
886+
}
887+
]
888+
if self.page_size_query_param is not None:
889+
parameters.append(
890+
{
891+
'name': self.page_size_query_param,
892+
'required': False,
893+
'in': 'query',
894+
'description': force_text(self.page_size_query_description),
895+
'schema': {
896+
'type': 'integer',
897+
},
898+
}
899+
)
900+
return parameters

0 commit comments

Comments
 (0)