Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix nested serializer schema rendering #6568

Closed
wants to merge 14 commits into from
Closed
Show file tree
Hide file tree
Changes from 13 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions docs/community/3.10-announcement.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@

# Django REST framework 3.10


* Reworked OpenAPI schema generation.
* Python 3 only.


## OpenAPI Schema Generation.

Since we first introduced schema support in Django REST Framework 3.5, OpenAPI has emerged as the widely adopted standard for modelling Web APIs.

This release deprecates the old CoreAPI based schema generation, and introduces improved OpenAPI schema generation in its place.

### Deprecating CoreAPI Schema Generation.

The in-built docs that were introduced in Django REST Framework v3.5 were built on CoreAPI. These are now deprecated. You may continue to use them but they will be **removed in Django REST Framework v 3.12**.

You should migrate to using the new OpenAPI based schema generation as soon as you can.


We have removed the old documentation for the CoreAPI based schema generation.
You may view them from here:

TOOD: Insert links:

* Tutorial 7: Schemas & client libraries
* Section from Documenting your API topic page.
* Includes API reference for `include_docs_urls()` and supporting functions.
* Schemas API Guide reference.

----

**TODO**:
- Currently, `SchemaView` will keep working.
- Hardcoded generator class.
- `generateschema` won't work... Do we need to fix that?
- Only introduced in v3.9. Not really useful for `coreapi`?
- Note on (≈private) import changes.
- Documented utility imports from `schemas` should be maintained.
- Sub-imports can move for the code changes.

----

1 change: 1 addition & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ pages:
- 'Contributing to REST framework': 'community/contributing.md'
- 'Project management': 'community/project-management.md'
- 'Release Notes': 'community/release-notes.md'
- '3.10 Announcement': 'community/3.10-announcement.md'
- '3.9 Announcement': 'community/3.9-announcement.md'
- '3.8 Announcement': 'community/3.8-announcement.md'
- '3.7 Announcement': 'community/3.7-announcement.md'
Expand Down
29 changes: 29 additions & 0 deletions rest_framework/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ def get_schema_fields(self, view):
assert coreschema is not None, 'coreschema must be installed to use `get_schema_fields()`'
return []

def get_schema_operation_parameters(self, view):
return []


class SearchFilter(BaseFilterBackend):
# The URL query parameter used for the search.
Expand Down Expand Up @@ -159,6 +162,19 @@ def get_schema_fields(self, view):
)
]

def get_schema_operation_parameters(self, view):
return [
{
'name': self.search_param,
'required': False,
'in': 'query',
'description': force_text(self.search_description),
'schema': {
'type': 'string',
},
},
]


class OrderingFilter(BaseFilterBackend):
# The URL query parameter used for the ordering.
Expand Down Expand Up @@ -290,6 +306,19 @@ def get_schema_fields(self, view):
)
]

def get_schema_operation_parameters(self, view):
return [
{
'name': self.ordering_param,
'required': False,
'in': 'query',
'description': force_text(self.ordering_description),
'schema': {
'type': 'string',
},
},
]


class DjangoObjectPermissionsFilter(BaseFilterBackend):
"""
Expand Down
31 changes: 11 additions & 20 deletions rest_framework/management/commands/generateschema.py
Original file line number Diff line number Diff line change
@@ -1,24 +1,20 @@
from django.core.management.base import BaseCommand

from rest_framework.compat import coreapi
from rest_framework.renderers import (
CoreJSONRenderer, JSONOpenAPIRenderer, OpenAPIRenderer
)
from rest_framework.schemas.generators import SchemaGenerator
from rest_framework.compat import yaml
from rest_framework.schemas.openapi import SchemaGenerator
from rest_framework.utils import json


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

def add_arguments(self, parser):
parser.add_argument('--title', dest="title", default=None, type=str)
parser.add_argument('--title', dest="title", default='', type=str)
parser.add_argument('--url', dest="url", default=None, type=str)
parser.add_argument('--description', dest="description", default=None, type=str)
parser.add_argument('--format', dest="format", choices=['openapi', 'openapi-json', 'corejson'], default='openapi', type=str)
parser.add_argument('--format', dest="format", choices=['openapi', 'openapi-json'], default='openapi', type=str)

def handle(self, *args, **options):
assert coreapi is not None, 'coreapi must be installed.'

generator = SchemaGenerator(
url=options['url'],
title=options['title'],
Expand All @@ -27,15 +23,10 @@ def handle(self, *args, **options):

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

renderer = self.get_renderer(options['format'])
output = renderer.render(schema, renderer_context={})
self.stdout.write(output.decode('utf-8'))

def get_renderer(self, format):
renderer_cls = {
'corejson': CoreJSONRenderer,
'openapi': OpenAPIRenderer,
'openapi-json': JSONOpenAPIRenderer,
}[format]
# TODO: Handle via renderer? More options?
if options['format'] == 'openapi':
output = yaml.dump(schema, default_flow_style=False)
else:
output = json.dumps(schema, indent=2)

return renderer_cls()
self.stdout.write(output)
94 changes: 86 additions & 8 deletions rest_framework/pagination.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,9 @@ def get_schema_fields(self, view):
assert coreapi is not None, 'coreapi must be installed to use `get_schema_fields()`'
return []

def get_schema_operation_parameters(self, view):
return []


class PageNumberPagination(BasePagination):
"""
Expand Down Expand Up @@ -305,6 +308,32 @@ def get_schema_fields(self, view):
)
return fields

def get_schema_operation_parameters(self, view):
parameters = [
{
'name': self.page_query_param,
'required': False,
'in': 'query',
'description': force_text(self.page_query_description),
'schema': {
'type': 'integer',
},
},
]
if self.page_size_query_param is not None:
parameters.append(
{
'name': self.page_size_query_param,
'required': False,
'in': 'query',
'description': force_text(self.page_size_query_description),
'schema': {
'type': 'integer',
},
},
)
return parameters


class LimitOffsetPagination(BasePagination):
"""
Expand Down Expand Up @@ -434,6 +463,15 @@ def to_html(self):
context = self.get_html_context()
return template.render(context)

def get_count(self, queryset):
"""
Determine an object count, supporting either querysets or regular lists.
"""
try:
return queryset.count()
except (AttributeError, TypeError):
return len(queryset)

def get_schema_fields(self, view):
assert coreapi is not None, 'coreapi must be installed to use `get_schema_fields()`'
assert coreschema is not None, 'coreschema must be installed to use `get_schema_fields()`'
Expand All @@ -458,14 +496,28 @@ def get_schema_fields(self, view):
)
]

def get_count(self, queryset):
"""
Determine an object count, supporting either querysets or regular lists.
"""
try:
return queryset.count()
except (AttributeError, TypeError):
return len(queryset)
def get_schema_operation_parameters(self, view):
parameters = [
{
'name': self.limit_query_param,
'required': False,
'in': 'query',
'description': force_text(self.limit_query_description),
'schema': {
'type': 'integer',
},
},
{
'name': self.offset_query_param,
'required': False,
'in': 'query',
'description': force_text(self.offset_query_description),
'schema': {
'type': 'integer',
},
},
]
return parameters


class CursorPagination(BasePagination):
Expand Down Expand Up @@ -820,3 +872,29 @@ def get_schema_fields(self, view):
)
)
return fields

def get_schema_operation_parameters(self, view):
parameters = [
{
'name': self.cursor_query_param,
'required': False,
'in': 'query',
'description': force_text(self.cursor_query_description),
'schema': {
'type': 'integer',
},
}
]
if self.page_size_query_param is not None:
parameters.append(
{
'name': self.page_size_query_param,
'required': False,
'in': 'query',
'description': force_text(self.page_size_query_description),
'schema': {
'type': 'integer',
},
}
)
return parameters
4 changes: 2 additions & 2 deletions rest_framework/schemas/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@
"""
from rest_framework.settings import api_settings

from .generators import SchemaGenerator
from .inspectors import AutoSchema, DefaultSchema, ManualSchema # noqa
from .inspectors import DefaultSchema # noqa
from .coreapi import AutoSchema, ManualSchema, SchemaGenerator # noqa


def get_schema_view(
Expand Down
Loading