diff --git a/src/drf_yasg/inspectors/base.py b/src/drf_yasg/inspectors/base.py index d6f0bdc..a1fd22a 100644 --- a/src/drf_yasg/inspectors/base.py +++ b/src/drf_yasg/inspectors/base.py @@ -341,6 +341,9 @@ class ViewInspector(BaseInspector): #: methods that are assumed to require a request body determined by the view's ``serializer_class`` implicit_body_methods = ('PUT', 'PATCH', 'POST') + #: methods which are assumed to return a list of objects when present on non-detail endpoints + implicit_list_response_methods = ('GET',) + # real values set in __init__ to prevent import errors field_inspectors = [] #: filter_inspectors = [] #: @@ -375,19 +378,30 @@ class ViewInspector(BaseInspector): raise NotImplementedError("ViewInspector must implement get_operation()!") # methods below provided as default implementations for probing inspectors + def is_list_view(self): + """Determine whether this view is a list or a detail view. The difference between the two is that + detail views depend on a pk/id path parameter. Note that a non-detail view does not necessarily imply a list + reponse (:meth:`.has_list_response`), nor are list responses limited to non-detail views. + + For example, one might have a `/topic//posts` endpoint which is a detail view that has a list response. + + :rtype: bool""" + return is_list_view(self.path, self.method, self.view) + + def has_list_response(self): + """Determine whether this view returns multiple objects. By default this is any non-detail view + (see :meth:`.is_list_view`) whose request method is one of :attr:`.implicit_list_response_methods`. + + :rtype: bool + """ + return self.is_list_view() and (self.method.upper() in self.implicit_list_response_methods) def should_filter(self): """Determine whether filter backend parameters should be included for this request. :rtype: bool """ - if not getattr(self.view, 'filter_backends', None): - return False - - if self.method.lower() not in ["get", "delete"]: - return False - - return is_list_view(self.path, self.method, self.view) + return getattr(self.view, 'filter_backends', None) and self.has_list_response() def get_filter_parameters(self): """Return the parameters added to the view by its filter backends. @@ -408,13 +422,7 @@ class ViewInspector(BaseInspector): :rtype: bool """ - if not getattr(self.view, 'paginator', None): - return False - - if self.method.lower() != 'get': - return False - - return is_list_view(self.path, self.method, self.view) + return getattr(self.view, 'paginator', None) and self.has_list_response() def get_pagination_parameters(self): """Return the parameters added to the view by its paginator. diff --git a/src/drf_yasg/inspectors/view.py b/src/drf_yasg/inspectors/view.py index 3a94f6c..ecc65d4 100644 --- a/src/drf_yasg/inspectors/view.py +++ b/src/drf_yasg/inspectors/view.py @@ -208,7 +208,7 @@ class SwaggerAutoSchema(ViewInspector): default_schema = self.serializer_to_schema(default_schema) or '' if default_schema: - if is_list_view(self.path, self.method, self.view) and self.method.lower() == 'get': + if self.has_list_response(): default_schema = openapi.Schema(type=openapi.TYPE_ARRAY, items=default_schema) if self.should_page(): default_schema = self.get_paginated_response(default_schema) or default_schema diff --git a/tox.ini b/tox.ini index 4dd2267..115ef6b 100644 --- a/tox.ini +++ b/tox.ini @@ -1,7 +1,7 @@ [tox] minversion = 3.3.0 isolated_build = true -isolated_build_env=.package +isolated_build_env = .package # https://docs.djangoproject.com/en/dev/faq/install/#what-python-version-can-i-use-with-django envlist =