diff --git a/.gitignore b/.gitignore
index 0dffda8..205c9f1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -159,3 +159,5 @@ com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
+
+\.pytest_cache/
diff --git a/.idea/drf-yasg.iml b/.idea/drf-yasg.iml
index 3bbd482..2ecb6ac 100644
--- a/.idea/drf-yasg.iml
+++ b/.idea/drf-yasg.iml
@@ -16,6 +16,7 @@
+
diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
index 0aad453..9f03014 100644
--- a/.idea/inspectionProfiles/Project_Default.xml
+++ b/.idea/inspectionProfiles/Project_Default.xml
@@ -10,11 +10,12 @@
diff --git a/.idea/misc.xml b/.idea/misc.xml
index 940f30f..822f657 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -70,4 +70,7 @@
+
+
+
\ No newline at end of file
diff --git a/.travis.yml b/.travis.yml
index 9232afe..778e198 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -9,6 +9,7 @@ python:
env:
- DRF=3.7
+ - DRF=3.8
jobs:
include:
diff --git a/README.rst b/README.rst
index c0696fd..4908cfd 100644
--- a/README.rst
+++ b/README.rst
@@ -11,7 +11,7 @@ Generate **real** Swagger/OpenAPI 2.0 specifications from a Django Rest Framewor
Compatible with
-- **Django Rest Framework**: 3.7.7
+- **Django Rest Framework**: 3.7.7, 3.8.x
- **Django**: 1.11.x, 2.0.x
- **Python**: 2.7, 3.4, 3.5, 3.6
diff --git a/docs/custom_spec.rst b/docs/custom_spec.rst
index 3c9938d..0c08ca3 100644
--- a/docs/custom_spec.rst
+++ b/docs/custom_spec.rst
@@ -87,8 +87,8 @@ Where you can use the :func:`@swagger_auto_schema <.swagger_auto_schema>` decora
* for ``ViewSet``, ``GenericViewSet``, ``ModelViewSet``, because each viewset corresponds to multiple **paths**, you have
to decorate the *action methods*, i.e. ``list``, ``create``, ``retrieve``, etc. |br|
- Additionally, ``@list_route``\ s or ``@detail_route``\ s defined on the viewset, like function based api views, can
- respond to multiple HTTP methods and thus have multiple operations that must be decorated separately:
+ Additionally, ``@action``\ s, `@list_route``\ s or ``@detail_route``\ s defined on the viewset, like function based
+ api views, can respond to multiple HTTP methods and thus have multiple operations that must be decorated separately:
.. code-block:: python
@@ -96,13 +96,13 @@ Where you can use the :func:`@swagger_auto_schema <.swagger_auto_schema>` decora
class ArticleViewSet(viewsets.ModelViewSet):
# method or 'methods' can be skipped because the list_route only handles a single method (GET)
@swagger_auto_schema(operation_description='GET /articles/today/')
- @list_route(methods=['get'])
+ @action(detail=False, methods=['get'])
def today(self, request):
...
@swagger_auto_schema(method='get', operation_description="GET /articles/{id}/image/")
@swagger_auto_schema(method='post', operation_description="POST /articles/{id}/image/")
- @detail_route(methods=['get', 'post'], parser_classes=(MultiPartParser,))
+ @action(detail=True, methods=['get', 'post'], parser_classes=(MultiPartParser,))
def image(self, request, id=None):
...
diff --git a/requirements/tox.txt b/requirements/tox.txt
index f1661a0..722c32b 100644
--- a/requirements/tox.txt
+++ b/requirements/tox.txt
@@ -1,5 +1,5 @@
# requirements for building and running tox
-tox>=2.9.1
+tox>=2.9.1,<3.0.0
detox>=0.11
-r setup.txt
diff --git a/src/drf_yasg/utils.py b/src/drf_yasg/utils.py
index 3aa894b..9e1c5ed 100644
--- a/src/drf_yasg/utils.py
+++ b/src/drf_yasg/utils.py
@@ -114,7 +114,7 @@ def swagger_auto_schema(method=None, methods=None, auto_schema=unset, request_bo
# no overrides to set, no use in doing more work
return
- # if the method is a detail_route or list_route, it will have a bind_to_methods attribute
+ # if the method is an @action, it will have a bind_to_methods attribute
bind_to_methods = getattr(view_method, 'bind_to_methods', [])
# if the method is actually a function based view (@api_view), it will have a 'cls' attribute
view_cls = getattr(view_method, 'cls', None)
@@ -126,7 +126,7 @@ def swagger_auto_schema(method=None, methods=None, auto_schema=unset, request_bo
_methods = methods
if methods or method:
assert available_methods or http_method_names, "`method` or `methods` can only be specified " \
- "on @detail_route or @api_view views"
+ "on @action or @api_view views"
assert bool(methods) != bool(method), "specify either method or methods"
assert not isinstance(methods, str), "`methods` expects to receive a list of methods;" \
" use `method` for a single argument"
@@ -138,13 +138,13 @@ def swagger_auto_schema(method=None, methods=None, auto_schema=unset, request_bo
assert not any(mth in existing_data for mth in _methods), "http method defined multiple times"
if available_methods:
- # detail_route, list_route or api_view
+ # action or api_view
assert bool(http_method_names) != bool(bind_to_methods), "this should never happen"
if len(available_methods) > 1:
assert _methods, \
- "on multi-method api_view, detail_route or list_route, you must specify swagger_auto_schema on " \
- "a per-method basis using one of the `method` or `methods` arguments"
+ "on multi-method api_view, action, detail_route or list_route, you must specify " \
+ "swagger_auto_schema on a per-method basis using one of the `method` or `methods` arguments"
else:
# for a single-method view we assume that single method as the decorator target
_methods = _methods or available_methods
@@ -156,8 +156,9 @@ def swagger_auto_schema(method=None, methods=None, auto_schema=unset, request_bo
view_method._swagger_auto_schema = existing_data
else:
assert not _methods, \
- "the methods argument should only be specified when decorating a detail_route or list_route; you " \
- "should also ensure that you put the swagger_auto_schema decorator AFTER (above) the _route decorator"
+ "the methods argument should only be specified when decorating an action, detail_route or " \
+ "list_route; you should also ensure that you put the swagger_auto_schema decorator " \
+ "AFTER (above) the _route decorator"
assert not existing_data, "swagger_auto_schema applied twice to method"
view_method._swagger_auto_schema = data
@@ -183,7 +184,7 @@ def is_list_view(path, method, view):
return True
if action in ('retrieve', 'update', 'partial_update', 'destroy') or detail is True or suffix == 'Instance':
- # a detail_route is surely not a list route
+ # a detail action is surely not a list route
return False
# for GenericAPIView, if it's a detail view it can't also be a list view
diff --git a/testproj/articles/views.py b/testproj/articles/views.py
index 57cf910..9d91983 100644
--- a/testproj/articles/views.py
+++ b/testproj/articles/views.py
@@ -3,6 +3,7 @@ import datetime
from django.utils.decorators import method_decorator
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework import viewsets
+# noinspection PyDeprecation
from rest_framework.decorators import detail_route, list_route
from rest_framework.filters import OrderingFilter
from rest_framework.pagination import LimitOffsetPagination
@@ -89,23 +90,48 @@ class ArticleViewSet(viewsets.ModelViewSet):
swagger_schema = NoTitleAutoSchema
- @swagger_auto_schema(auto_schema=NoPagingAutoSchema, filter_inspectors=[DjangoFilterDescriptionInspector])
- @list_route(methods=['get'])
- def today(self, request):
- today_min = datetime.datetime.combine(datetime.date.today(), datetime.time.min)
- today_max = datetime.datetime.combine(datetime.date.today(), datetime.time.max)
- articles = self.get_queryset().filter(date_created__range=(today_min, today_max)).all()
- serializer = self.serializer_class(articles, many=True)
- return Response(serializer.data)
+ try:
+ from rest_framework.decorators import action
- @swagger_auto_schema(method='get', operation_description="image GET description override")
- @swagger_auto_schema(method='post', request_body=serializers.ImageUploadSerializer)
- @detail_route(methods=['get', 'post'], parser_classes=(MultiPartParser,))
- def image(self, request, slug=None):
- """
- image method docstring
- """
- pass
+ @swagger_auto_schema(auto_schema=NoPagingAutoSchema, filter_inspectors=[DjangoFilterDescriptionInspector])
+ @action(detail=False, methods=['get'])
+ def today(self, request):
+ today_min = datetime.datetime.combine(datetime.date.today(), datetime.time.min)
+ today_max = datetime.datetime.combine(datetime.date.today(), datetime.time.max)
+ articles = self.get_queryset().filter(date_created__range=(today_min, today_max)).all()
+ serializer = self.serializer_class(articles, many=True)
+ return Response(serializer.data)
+
+ @swagger_auto_schema(method='get', operation_description="image GET description override")
+ @swagger_auto_schema(method='post', request_body=serializers.ImageUploadSerializer)
+ @action(detail=True, methods=['get', 'post'], parser_classes=(MultiPartParser,))
+ def image(self, request, slug=None):
+ """
+ image method docstring
+ """
+ pass
+ except ImportError:
+ action = None
+
+ # noinspection PyDeprecation
+ @swagger_auto_schema(auto_schema=NoPagingAutoSchema, filter_inspectors=[DjangoFilterDescriptionInspector])
+ @list_route(methods=['get'])
+ def today(self, request):
+ today_min = datetime.datetime.combine(datetime.date.today(), datetime.time.min)
+ today_max = datetime.datetime.combine(datetime.date.today(), datetime.time.max)
+ articles = self.get_queryset().filter(date_created__range=(today_min, today_max)).all()
+ serializer = self.serializer_class(articles, many=True)
+ return Response(serializer.data)
+
+ # noinspection PyDeprecation
+ @swagger_auto_schema(method='get', operation_description="image GET description override")
+ @swagger_auto_schema(method='post', request_body=serializers.ImageUploadSerializer)
+ @detail_route(methods=['get', 'post'], parser_classes=(MultiPartParser,))
+ def image(self, request, slug=None):
+ """
+ image method docstring
+ """
+ pass
def update(self, request, *args, **kwargs):
"""update method docstring"""
diff --git a/tox.ini b/tox.ini
index 714a3be..ef97c6b 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,13 +1,14 @@
[tox]
envlist =
py27-django111-drf37,
- py{34,35,36}-django{111,20}-drf37,
+ py{34,35,36}-django{111,20}-drf{37,38},
py36-django20-drfmaster,
lint, docs
[travis:env]
DRF =
3.7: drf37
+ 3.8: drf38
master: drfmaster
[testenv]
@@ -16,6 +17,7 @@ deps =
django20: Django>=2.0,<2.1
drf37: djangorestframework>=3.7.7,<3.8
+ drf38: djangorestframework>=3.8.0,<3.9
# test with the latest build of django-rest-framework to get early warning of compatibility issues
drfmaster: https://github.com/encode/django-rest-framework/archive/master.tar.gz