Call APIView get_ methods instead of direct attribute access

queryset -> get_queryset
renderer_classes -> get_renderers
parser_classes -> get_parsers
This commit is contained in:
Cristi Vîjdea
2018-12-23 16:57:01 +02:00
parent 04d61b9d97
commit bebcc982e6
8 changed files with 124 additions and 43 deletions
+47 -6
View File
@@ -4,7 +4,7 @@ import logging
from rest_framework import serializers
from .. import openapi
from ..utils import force_real_str, get_field_default, is_list_view
from ..utils import force_real_str, get_field_default, get_object_classes, is_list_view
#: Sentinel value that inspectors must return to signal that they do not know how to handle an object
NotHandled = object()
@@ -12,14 +12,38 @@ NotHandled = object()
logger = logging.getLogger(__name__)
def call_view_method(view, method_name, fallback_attr=None, default=None):
"""Call a view method which might throw an exception. If an exception is thrown, log an informative error message
and return the value of fallback_attr, or default if not present.
:param rest_framework.views.APIView view:
:param str method_name: name of a method on the view
:param str fallback_attr: name of an attribute on the view to fall back on, if calling the method fails
:param default: default value if all else fails
:return: view method's return value, or value of view's fallback_attr, or default
"""
if hasattr(view, method_name):
try:
return getattr(view, method_name)()
except Exception: # pragma: no cover
logger.warning("view's %s.get_parsers raised exception during schema generation; use "
"`getattr(self, 'swagger_fake_view', False)` to detect and short-circuit this",
type(view).__name__, exc_info=True)
if fallback_attr and hasattr(view, fallback_attr):
return getattr(view, fallback_attr)
return default
class BaseInspector(object):
def __init__(self, view, path, method, components, request):
"""
:param view: the view associated with this endpoint
:param rest_framework.views.APIView view: the view associated with this endpoint
:param str path: the path component of the operation URL
:param str method: the http method of the operation
:param openapi.ReferenceResolver components: referenceable components
:param Request request: the request made against the schema view; can be None
:param rest_framework.request.Request request: the request made against the schema view; can be None
"""
self.view = view
self.path = path
@@ -81,6 +105,22 @@ class BaseInspector(object):
return result
def get_renderer_classes(self):
"""Get the renderer classes of this view by calling `get_renderers`.
:return: renderer classes
:rtype: list[type[rest_framework.renderers.BaseRenderer]]
"""
return get_object_classes(call_view_method(self.view, 'get_renderers', 'renderer_classes', []))
def get_parser_classes(self):
"""Get the parser classes of this view by calling `get_parsers`.
:return: parser classes
:rtype: list[type[rest_framework.parsers.BaseParser]]
"""
return get_object_classes(call_view_method(self.view, 'get_parsers', 'parser_classes', []))
class PaginatorInspector(BaseInspector):
"""Base inspector for paginators.
@@ -335,7 +375,7 @@ class ViewInspector(BaseInspector):
return []
fields = []
for filter_backend in self.view.filter_backends:
for filter_backend in getattr(self.view, 'filter_backends'):
fields += self.probe_inspectors(self.filter_inspectors, 'get_filter_parameters', filter_backend()) or []
return fields
@@ -361,7 +401,8 @@ class ViewInspector(BaseInspector):
if not self.should_page():
return []
return self.probe_inspectors(self.paginator_inspectors, 'get_paginator_parameters', self.view.paginator) or []
return self.probe_inspectors(self.paginator_inspectors, 'get_paginator_parameters',
getattr(self.view, 'paginator')) or []
def serializer_to_schema(self, serializer):
"""Convert a serializer to an OpenAPI :class:`.Schema`.
@@ -394,4 +435,4 @@ class ViewInspector(BaseInspector):
:rtype: openapi.Schema
"""
return self.probe_inspectors(self.paginator_inspectors, 'get_paginated_response',
self.view.paginator, response_schema=response_schema)
getattr(self.view, 'paginator'), response_schema=response_schema)
+4 -4
View File
@@ -14,7 +14,7 @@ from rest_framework.settings import api_settings as rest_framework_settings
from .. import openapi
from ..errors import SwaggerGenerationError
from ..utils import decimal_as_float, filter_none, get_serializer_class, get_serializer_ref_name
from .base import FieldInspector, NotHandled, SerializerInspector
from .base import FieldInspector, NotHandled, SerializerInspector, call_view_method
try:
import typing
@@ -177,7 +177,7 @@ def get_queryset_from_view(view, serializer=None):
:return: queryset or ``None``
"""
try:
queryset = getattr(view, 'queryset', None)
queryset = call_view_method(view, 'get_queryset', 'queryset', None)
if queryset is not None and serializer is not None:
# make sure the view is actually using *this* serializer
@@ -733,8 +733,8 @@ class CamelCaseJSONFilter(FieldInspector):
if CamelCaseJSONParser and CamelCaseJSONRenderer:
def is_camel_case(self):
return (
any(issubclass(parser, CamelCaseJSONParser) for parser in self.view.parser_classes) or
any(issubclass(renderer, CamelCaseJSONRenderer) for renderer in self.view.renderer_classes)
any(issubclass(parser, CamelCaseJSONParser) for parser in self.get_parser_classes()) or
any(issubclass(renderer, CamelCaseJSONRenderer) for renderer in self.get_renderer_classes())
)
else:
def is_camel_case(self):
+8 -16
View File
@@ -12,9 +12,9 @@ from ..utils import (
filter_none, force_real_str, force_serializer_instance, get_consumes, get_produces, guess_response_status,
is_list_view, merge_params, no_body, param_list_to_odict
)
from .base import ViewInspector
from .base import ViewInspector, call_view_method
log = logging.getLogger(__name__)
logger = logging.getLogger(__name__)
class SwaggerAutoSchema(ViewInspector):
@@ -103,21 +103,12 @@ class SwaggerAutoSchema(ViewInspector):
"""Return the serializer as defined by the view's ``get_serializer()`` method.
:return: the view's ``Serializer``
:rtype: rest_framework.serializers.Serializer
"""
if not hasattr(self.view, 'get_serializer'):
return None
try:
return self.view.get_serializer()
except Exception:
log.warning("view's get_serializer raised exception (%s %s %s)",
self.method, self.path, type(self.view).__name__, exc_info=True)
return None
return call_view_method(self.view, 'get_serializer')
def _get_request_body_override(self):
"""Parse the request_body key in the override dict. This method is not public API.
:return:
"""
"""Parse the request_body key in the override dict. This method is not public API."""
body_override = self.overrides.get('request_body', None)
if body_override is not None:
@@ -136,6 +127,7 @@ class SwaggerAutoSchema(ViewInspector):
"""Return the request serializer (used for parsing the request payload) for this endpoint.
:return: the request serializer, or one of :class:`.Schema`, :class:`.SchemaRef`, ``None``
:rtype: rest_framework.serializers.Serializer
"""
body_override = self._get_request_body_override()
@@ -430,11 +422,11 @@ class SwaggerAutoSchema(ViewInspector):
:rtype: list[str]
"""
return get_consumes(getattr(self.view, 'parser_classes', []))
return get_consumes(self.get_parser_classes())
def get_produces(self):
"""Return the MIME types this endpoint can produce.
:rtype: list[str]
"""
return get_produces(getattr(self.view, 'renderer_classes', []))
return get_produces(self.get_renderer_classes())