integer primary key, doc, cleanup, lint, test

This commit is contained in:
Thorsten Franzel 2019-12-14 00:00:39 +01:00
parent 1535ee2a3c
commit d8c6821563
3 changed files with 53 additions and 15 deletions

View File

@ -14,16 +14,18 @@ from django.db import models
from django.utils.encoding import force_str from django.utils.encoding import force_str
from django.utils.module_loading import import_string from django.utils.module_loading import import_string
from rest_framework import exceptions, renderers, serializers, permissions from rest_framework import exceptions, permissions, renderers, serializers
from rest_framework.compat import uritemplate from rest_framework.compat import uritemplate
from rest_framework.fields import _UnvalidatedField, empty from rest_framework.fields import _UnvalidatedField, empty
from rest_framework.schemas.openapi_utils import (
TYPE_MAPPING, PolymorphicResponse
)
from rest_framework.settings import api_settings from rest_framework.settings import api_settings
from rest_framework.schemas.openapi_utils import TYPE_MAPPING, PolymorphicResponse
from .generators import BaseSchemaGenerator from .generators import BaseSchemaGenerator
from .inspectors import ViewInspector from .inspectors import ViewInspector
from .utils import get_pk_description, is_list_view from .utils import get_pk_description, is_list_view
AUTHENTICATION_SCHEMES = { AUTHENTICATION_SCHEMES = {
cls.authentication_class: cls cls.authentication_class: cls
for cls in [import_string(cls) for cls in api_settings.SCHEMA_AUTHENTICATION_CLASSES] for cls in [import_string(cls) for cls in api_settings.SCHEMA_AUTHENTICATION_CLASSES]
@ -180,7 +182,7 @@ class AutoSchema(ViewInspector):
action_or_method = getattr(self.view, getattr(self.view, 'action', method.lower()), None) action_or_method = getattr(self.view, getattr(self.view, 'action', method.lower()), None)
view_doc = inspect.getdoc(self.view) or '' view_doc = inspect.getdoc(self.view) or ''
action_doc = inspect.getdoc(action_or_method) or '' action_doc = inspect.getdoc(action_or_method) or ''
return view_doc + '\n\n' + action_doc if action_doc else view_doc return action_doc or view_doc
def get_auth(self, path, method): def get_auth(self, path, method):
""" override this for custom behaviour """ """ override this for custom behaviour """
@ -262,8 +264,10 @@ class AutoSchema(ViewInspector):
elif model_field is not None and model_field.primary_key: elif model_field is not None and model_field.primary_key:
description = get_pk_description(model, model_field) description = get_pk_description(model, model_field)
# TODO cover more cases # TODO are there more relevant PK base classes?
if isinstance(model_field, models.UUIDField): if isinstance(model_field, models.IntegerField):
schema = TYPE_MAPPING[int]
elif isinstance(model_field, models.UUIDField):
schema = TYPE_MAPPING[UUID] schema = TYPE_MAPPING[UUID]
parameter = { parameter = {
@ -498,7 +502,7 @@ class AutoSchema(ViewInspector):
result = { result = {
'properties': properties 'properties': properties
} }
if required and method != 'PATCH' and not nested: if required and method != 'PATCH':
result['required'] = required result['required'] = required
return result return result
@ -648,7 +652,6 @@ class AutoSchema(ViewInspector):
} }
return {'200': self._get_response_for_code(path, method, schema)} return {'200': self._get_response_for_code(path, method, schema)}
def _get_response_for_code(self, path, method, serializer_instance): def _get_response_for_code(self, path, method, serializer_instance):
if not serializer_instance: if not serializer_instance:
return {'description': 'No response body'} return {'description': 'No response body'}
@ -702,7 +705,7 @@ class AutoSchema(ViewInspector):
if name.endswith('Serializer'): if name.endswith('Serializer'):
name = name[:-10] name = name[:-10]
if method == 'PATCH' and not nested: if method == 'PATCH' and not serializer.read_only: # TODO maybe even use serializer.partial
name = 'Patched' + name name = 'Patched' + name
return name return name

View File

@ -1,8 +1,8 @@
import inspect import inspect
import warnings import warnings
from datetime import date, datetime
from decimal import Decimal from decimal import Decimal
from uuid import UUID from uuid import UUID
from datetime import datetime, date
from rest_framework import authentication from rest_framework import authentication
from rest_framework.settings import api_settings from rest_framework.settings import api_settings
@ -103,6 +103,7 @@ def extend_schema(
TODO some heavy explaining TODO some heavy explaining
:param operation: :param operation:
:param operation_id:
:param extra_parameters: :param extra_parameters:
:param responses: :param responses:
:param request: :param request:

View File

@ -1,14 +1,19 @@
import pytest import pytest
from django.conf.urls import url from django.conf.urls import url
from django.db import models
from django.test import RequestFactory, TestCase, override_settings from django.test import RequestFactory, TestCase, override_settings
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from rest_framework import filters, generics, pagination, routers, serializers from rest_framework import (
filters, generics, pagination, routers, serializers, viewsets
)
from rest_framework.compat import uritemplate from rest_framework.compat import uritemplate
from rest_framework.parsers import JSONParser, MultiPartParser from rest_framework.parsers import JSONParser, MultiPartParser
from rest_framework.renderers import JSONRenderer from rest_framework.renderers import JSONRenderer
from rest_framework.request import Request from rest_framework.request import Request
from rest_framework.schemas.openapi import AutoSchema, SchemaGenerator, ComponentRegistry from rest_framework.schemas.openapi import (
AutoSchema, ComponentRegistry, SchemaGenerator
)
from . import views from . import views
@ -126,7 +131,7 @@ class TestOperationIntrospection(TestCase):
operation = inspector.get_operation(path, method) operation = inspector.get_operation(path, method)
assert operation == { assert operation == {
'operationId': 'example_retrieve', 'operationId': 'example_retrieve',
'description': '\n\nA description of my GET operation.', 'description': 'A description of my GET operation.',
'parameters': [ 'parameters': [
{ {
'name': 'id', 'name': 'id',
@ -294,8 +299,7 @@ class TestOperationIntrospection(TestCase):
inspector = AutoSchema() inspector = AutoSchema()
inspector.view = view inspector.view = view
inspector.init(registry) inspector.init(registry)
inspector.get_operation(path, method)
operation = inspector.get_operation(path, method)
example_schema = registry.schemas['Example'] example_schema = registry.schemas['Example']
nested_schema = registry.schemas['Nested'] nested_schema = registry.schemas['Nested']
@ -681,6 +685,36 @@ class TestOperationIntrospection(TestCase):
assert properties['ip']['type'] == 'string' assert properties['ip']['type'] == 'string'
assert 'format' not in properties['ip'] assert 'format' not in properties['ip']
def test_modelviewset(self):
class ExampleModel(models.Model):
text = models.TextField()
class ExampleSerializer(serializers.ModelSerializer):
class Meta:
model = ExampleModel
fields = ['id', 'text']
class ExampleViewSet(viewsets.ModelViewSet):
serializer_class = ExampleSerializer
queryset = ExampleModel.objects.none()
from django.urls import path, include
router = routers.DefaultRouter()
router.register(r'example', ExampleViewSet)
generator = SchemaGenerator(patterns=[
path(r'api/', include(router.urls))
])
generator._initialise_endpoints()
schema = generator.get_schema(request=None, public=True)
assert list(schema['paths']['/api/example/'].keys()) == ['get', 'post']
assert list(schema['paths']['/api/example/{id}/'].keys()) == ['get', 'put', 'patch', 'delete']
assert list(schema['components']['schemas'].keys()) == ['Example', 'PatchedExample']
# TODO do more checks
@pytest.mark.skipif(uritemplate is None, reason='uritemplate not installed.') @pytest.mark.skipif(uritemplate is None, reason='uritemplate not installed.')
@override_settings(REST_FRAMEWORK={'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.openapi.AutoSchema'}) @override_settings(REST_FRAMEWORK={'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.openapi.AutoSchema'})