diff --git a/src/drf_yasg/generators.py b/src/drf_yasg/generators.py index 689192c..8364666 100644 --- a/src/drf_yasg/generators.py +++ b/src/drf_yasg/generators.py @@ -31,8 +31,7 @@ class EndpointEnumerator(_EndpointEnumerator): def get_path_from_regex(self, path_regex): if path_regex.endswith(')'): - logger.warning("url pattern does not end in $ ('%s') - unexpected things might happen", - path_regex) + logger.warning("url pattern does not end in $ ('%s') - unexpected things might happen", path_regex) return self.unescape_path(super(EndpointEnumerator, self).get_path_from_regex(path_regex)) def should_include_endpoint(self, path, callback, app_name='', namespace='', url_name=None): diff --git a/src/drf_yasg/inspectors/field.py b/src/drf_yasg/inspectors/field.py index e543eee..a9b56df 100644 --- a/src/drf_yasg/inspectors/field.py +++ b/src/drf_yasg/inspectors/field.py @@ -13,7 +13,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_ref_name +from ..utils import decimal_as_float, filter_none, get_serializer_ref_name, get_serializer_class from .base import FieldInspector, NotHandled, SerializerInspector try: @@ -107,7 +107,7 @@ class InlineSerializerInspector(SerializerInspector): ) if not ref_name and 'title' in result: # on an inline model, the title is derived from the field name - # but is visually displayed like the model name, which is confusing + # but is visno coverually displayed like the model name, which is confusing # it is better to just remove title from inline models del result.title @@ -117,7 +117,13 @@ class InlineSerializerInspector(SerializerInspector): return make_schema_definition() definitions = self.components.with_scope(openapi.SCHEMA_DEFINITIONS) - definitions.setdefault(ref_name, make_schema_definition) + actual_schema = definitions.setdefault(ref_name, make_schema_definition) + actual_serializer = get_serializer_class(getattr(actual_schema, '_serializer', None)) + this_serializer = get_serializer_class(field) + if actual_serializer and actual_serializer != this_serializer: # pragma: no cover + logger.warning("Schema for %s will override distinct serializer %s because they " + "share the same ref_name", actual_serializer, this_serializer) + return openapi.SchemaRef(definitions, ref_name) return NotHandled diff --git a/src/drf_yasg/openapi.py b/src/drf_yasg/openapi.py index 177adb3..e864da5 100644 --- a/src/drf_yasg/openapi.py +++ b/src/drf_yasg/openapi.py @@ -1,3 +1,4 @@ +import logging import re from collections import OrderedDict @@ -7,6 +8,8 @@ from inflection import camelize from .utils import filter_none +logger = logging.getLogger(__name__) + TYPE_OBJECT = "object" #: TYPE_STRING = "string" #: TYPE_NUMBER = "number" #: @@ -628,8 +631,13 @@ class ReferenceResolver(object): ret = self.getdefault(name, None, scope) if ret is None: ret = maker() + value = self.getdefault(name, None, scope) assert ret is not None, "maker returned None; referenced objects cannot be None/null" - self.set(name, ret, scope) + if value is None: + self.set(name, ret, scope) + elif value != ret: + logger.debug("during setdefault, maker for %s inserted a value and returned a different value", name) + ret = value return ret diff --git a/src/drf_yasg/utils.py b/src/drf_yasg/utils.py index f0018da..1ba905f 100644 --- a/src/drf_yasg/utils.py +++ b/src/drf_yasg/utils.py @@ -274,6 +274,7 @@ def force_serializer_instance(serializer): :param serializer: serializer class or instance :return: serializer instance + :rtype: serializers.BaseSerializer """ if inspect.isclass(serializer): assert issubclass(serializer, serializers.BaseSerializer), "Serializer required, not %s" % serializer.__name__ @@ -284,6 +285,26 @@ def force_serializer_instance(serializer): return serializer +def get_serializer_class(serializer): + """Given a ``Serializer`` class or intance, return the ``Serializer`` class. If `serializer` is not a ``Serializer`` + class or instance, raises an assertion error. + + :param serializer: serializer class or instance, or ``None`` + :return: serializer class + :rtype: type[serializers.BaseSerializer] + """ + if serializer is None: + return None + + if inspect.isclass(serializer): + assert issubclass(serializer, serializers.BaseSerializer), "Serializer required, not %s" % serializer.__name__ + return serializer + + assert isinstance(serializer, serializers.BaseSerializer), \ + "Serializer class or instance required, not %s" % type(serializer).__name__ + return type(serializer) + + def get_consumes(parser_classes): """Extract ``consumes`` MIME types from a list of parser classes. @@ -336,7 +357,7 @@ def get_serializer_ref_name(serializer): if hasattr(serializer_meta, 'ref_name'): ref_name = serializer_meta.ref_name elif serializer_name == 'NestedSerializer' and isinstance(serializer, serializers.ModelSerializer): - logger.debug("Forcing inline output for ModelSerializer named 'NestedSerializer': " + str(serializer)) + logger.debug("Forcing inline output for ModelSerializer named 'NestedSerializer':\n" + str(serializer)) ref_name = None else: ref_name = serializer_name