Test with Django Rest Framework 3.8 (#96)
* Add djangorestframework 3.8 to tox and travis * Add @action tests * Limit tox to <3.0.0openapi3
parent
6f7d14fdb2
commit
7270154828
|
|
@ -159,3 +159,5 @@ com_crashlytics_export_strings.xml
|
||||||
crashlytics.properties
|
crashlytics.properties
|
||||||
crashlytics-build.properties
|
crashlytics-build.properties
|
||||||
fabric.properties
|
fabric.properties
|
||||||
|
|
||||||
|
\.pytest_cache/
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@
|
||||||
<content url="file://$MODULE_DIR$">
|
<content url="file://$MODULE_DIR$">
|
||||||
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||||
<sourceFolder url="file://$MODULE_DIR$/testproj" isTestSource="false" />
|
<sourceFolder url="file://$MODULE_DIR$/testproj" isTestSource="false" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/docs/_build" />
|
||||||
<excludeFolder url="file://$MODULE_DIR$/venv" />
|
<excludeFolder url="file://$MODULE_DIR$/venv" />
|
||||||
</content>
|
</content>
|
||||||
<orderEntry type="jdk" jdkName="Python 3.6 (drf-yasg)" jdkType="Python SDK" />
|
<orderEntry type="jdk" jdkName="Python 3.6 (drf-yasg)" jdkType="Python SDK" />
|
||||||
|
|
|
||||||
|
|
@ -10,11 +10,12 @@
|
||||||
<inspection_tool class="PyCompatibilityInspection" enabled="true" level="WARNING" enabled_by_default="true">
|
<inspection_tool class="PyCompatibilityInspection" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
<option name="ourVersions">
|
<option name="ourVersions">
|
||||||
<value>
|
<value>
|
||||||
<list size="4">
|
<list size="5">
|
||||||
<item index="0" class="java.lang.String" itemvalue="2.7" />
|
<item index="0" class="java.lang.String" itemvalue="2.7" />
|
||||||
<item index="1" class="java.lang.String" itemvalue="3.4" />
|
<item index="1" class="java.lang.String" itemvalue="3.4" />
|
||||||
<item index="2" class="java.lang.String" itemvalue="3.5" />
|
<item index="2" class="java.lang.String" itemvalue="3.5" />
|
||||||
<item index="3" class="java.lang.String" itemvalue="3.6" />
|
<item index="3" class="java.lang.String" itemvalue="3.6" />
|
||||||
|
<item index="4" class="java.lang.String" itemvalue="3.7" />
|
||||||
</list>
|
</list>
|
||||||
</value>
|
</value>
|
||||||
</option>
|
</option>
|
||||||
|
|
|
||||||
|
|
@ -70,4 +70,7 @@
|
||||||
</LinkMapSettings>
|
</LinkMapSettings>
|
||||||
</component>
|
</component>
|
||||||
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.6 (drf-yasg)" project-jdk-type="Python SDK" />
|
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.6 (drf-yasg)" project-jdk-type="Python SDK" />
|
||||||
|
<component name="PythonCompatibilityInspectionAdvertiser">
|
||||||
|
<option name="version" value="3" />
|
||||||
|
</component>
|
||||||
</project>
|
</project>
|
||||||
|
|
@ -9,6 +9,7 @@ python:
|
||||||
|
|
||||||
env:
|
env:
|
||||||
- DRF=3.7
|
- DRF=3.7
|
||||||
|
- DRF=3.8
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
include:
|
include:
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ Generate **real** Swagger/OpenAPI 2.0 specifications from a Django Rest Framewor
|
||||||
|
|
||||||
Compatible with
|
Compatible with
|
||||||
|
|
||||||
- **Django Rest Framework**: 3.7.7
|
- **Django Rest Framework**: 3.7.7, 3.8.x
|
||||||
- **Django**: 1.11.x, 2.0.x
|
- **Django**: 1.11.x, 2.0.x
|
||||||
- **Python**: 2.7, 3.4, 3.5, 3.6
|
- **Python**: 2.7, 3.4, 3.5, 3.6
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
* 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|
|
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
|
Additionally, ``@action``\ s, `@list_route``\ s or ``@detail_route``\ s defined on the viewset, like function based
|
||||||
respond to multiple HTTP methods and thus have multiple operations that must be decorated separately:
|
api views, can respond to multiple HTTP methods and thus have multiple operations that must be decorated separately:
|
||||||
|
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
@ -96,13 +96,13 @@ Where you can use the :func:`@swagger_auto_schema <.swagger_auto_schema>` decora
|
||||||
class ArticleViewSet(viewsets.ModelViewSet):
|
class ArticleViewSet(viewsets.ModelViewSet):
|
||||||
# method or 'methods' can be skipped because the list_route only handles a single method (GET)
|
# method or 'methods' can be skipped because the list_route only handles a single method (GET)
|
||||||
@swagger_auto_schema(operation_description='GET /articles/today/')
|
@swagger_auto_schema(operation_description='GET /articles/today/')
|
||||||
@list_route(methods=['get'])
|
@action(detail=False, methods=['get'])
|
||||||
def today(self, request):
|
def today(self, request):
|
||||||
...
|
...
|
||||||
|
|
||||||
@swagger_auto_schema(method='get', operation_description="GET /articles/{id}/image/")
|
@swagger_auto_schema(method='get', operation_description="GET /articles/{id}/image/")
|
||||||
@swagger_auto_schema(method='post', operation_description="POST /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):
|
def image(self, request, id=None):
|
||||||
...
|
...
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
# requirements for building and running tox
|
# requirements for building and running tox
|
||||||
tox>=2.9.1
|
tox>=2.9.1,<3.0.0
|
||||||
detox>=0.11
|
detox>=0.11
|
||||||
|
|
||||||
-r setup.txt
|
-r setup.txt
|
||||||
|
|
|
||||||
|
|
@ -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
|
# no overrides to set, no use in doing more work
|
||||||
return
|
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', [])
|
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
|
# if the method is actually a function based view (@api_view), it will have a 'cls' attribute
|
||||||
view_cls = getattr(view_method, 'cls', None)
|
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
|
_methods = methods
|
||||||
if methods or method:
|
if methods or method:
|
||||||
assert available_methods or http_method_names, "`method` or `methods` can only be specified " \
|
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 bool(methods) != bool(method), "specify either method or methods"
|
||||||
assert not isinstance(methods, str), "`methods` expects to receive a list of methods;" \
|
assert not isinstance(methods, str), "`methods` expects to receive a list of methods;" \
|
||||||
" use `method` for a single argument"
|
" 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"
|
assert not any(mth in existing_data for mth in _methods), "http method defined multiple times"
|
||||||
|
|
||||||
if available_methods:
|
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"
|
assert bool(http_method_names) != bool(bind_to_methods), "this should never happen"
|
||||||
|
|
||||||
if len(available_methods) > 1:
|
if len(available_methods) > 1:
|
||||||
assert _methods, \
|
assert _methods, \
|
||||||
"on multi-method api_view, detail_route or list_route, you must specify swagger_auto_schema on " \
|
"on multi-method api_view, action, detail_route or list_route, you must specify " \
|
||||||
"a per-method basis using one of the `method` or `methods` arguments"
|
"swagger_auto_schema on a per-method basis using one of the `method` or `methods` arguments"
|
||||||
else:
|
else:
|
||||||
# for a single-method view we assume that single method as the decorator target
|
# for a single-method view we assume that single method as the decorator target
|
||||||
_methods = _methods or available_methods
|
_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
|
view_method._swagger_auto_schema = existing_data
|
||||||
else:
|
else:
|
||||||
assert not _methods, \
|
assert not _methods, \
|
||||||
"the methods argument should only be specified when decorating a detail_route or list_route; you " \
|
"the methods argument should only be specified when decorating an action, detail_route or " \
|
||||||
"should also ensure that you put the swagger_auto_schema decorator AFTER (above) the _route decorator"
|
"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"
|
assert not existing_data, "swagger_auto_schema applied twice to method"
|
||||||
view_method._swagger_auto_schema = data
|
view_method._swagger_auto_schema = data
|
||||||
|
|
||||||
|
|
@ -183,7 +184,7 @@ def is_list_view(path, method, view):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
if action in ('retrieve', 'update', 'partial_update', 'destroy') or detail is True or suffix == 'Instance':
|
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
|
return False
|
||||||
|
|
||||||
# for GenericAPIView, if it's a detail view it can't also be a list view
|
# for GenericAPIView, if it's a detail view it can't also be a list view
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ import datetime
|
||||||
from django.utils.decorators import method_decorator
|
from django.utils.decorators import method_decorator
|
||||||
from django_filters.rest_framework import DjangoFilterBackend
|
from django_filters.rest_framework import DjangoFilterBackend
|
||||||
from rest_framework import viewsets
|
from rest_framework import viewsets
|
||||||
|
# noinspection PyDeprecation
|
||||||
from rest_framework.decorators import detail_route, list_route
|
from rest_framework.decorators import detail_route, list_route
|
||||||
from rest_framework.filters import OrderingFilter
|
from rest_framework.filters import OrderingFilter
|
||||||
from rest_framework.pagination import LimitOffsetPagination
|
from rest_framework.pagination import LimitOffsetPagination
|
||||||
|
|
@ -89,6 +90,30 @@ class ArticleViewSet(viewsets.ModelViewSet):
|
||||||
|
|
||||||
swagger_schema = NoTitleAutoSchema
|
swagger_schema = NoTitleAutoSchema
|
||||||
|
|
||||||
|
try:
|
||||||
|
from rest_framework.decorators import action
|
||||||
|
|
||||||
|
@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])
|
@swagger_auto_schema(auto_schema=NoPagingAutoSchema, filter_inspectors=[DjangoFilterDescriptionInspector])
|
||||||
@list_route(methods=['get'])
|
@list_route(methods=['get'])
|
||||||
def today(self, request):
|
def today(self, request):
|
||||||
|
|
@ -98,6 +123,7 @@ class ArticleViewSet(viewsets.ModelViewSet):
|
||||||
serializer = self.serializer_class(articles, many=True)
|
serializer = self.serializer_class(articles, many=True)
|
||||||
return Response(serializer.data)
|
return Response(serializer.data)
|
||||||
|
|
||||||
|
# noinspection PyDeprecation
|
||||||
@swagger_auto_schema(method='get', operation_description="image GET description override")
|
@swagger_auto_schema(method='get', operation_description="image GET description override")
|
||||||
@swagger_auto_schema(method='post', request_body=serializers.ImageUploadSerializer)
|
@swagger_auto_schema(method='post', request_body=serializers.ImageUploadSerializer)
|
||||||
@detail_route(methods=['get', 'post'], parser_classes=(MultiPartParser,))
|
@detail_route(methods=['get', 'post'], parser_classes=(MultiPartParser,))
|
||||||
|
|
|
||||||
4
tox.ini
4
tox.ini
|
|
@ -1,13 +1,14 @@
|
||||||
[tox]
|
[tox]
|
||||||
envlist =
|
envlist =
|
||||||
py27-django111-drf37,
|
py27-django111-drf37,
|
||||||
py{34,35,36}-django{111,20}-drf37,
|
py{34,35,36}-django{111,20}-drf{37,38},
|
||||||
py36-django20-drfmaster,
|
py36-django20-drfmaster,
|
||||||
lint, docs
|
lint, docs
|
||||||
|
|
||||||
[travis:env]
|
[travis:env]
|
||||||
DRF =
|
DRF =
|
||||||
3.7: drf37
|
3.7: drf37
|
||||||
|
3.8: drf38
|
||||||
master: drfmaster
|
master: drfmaster
|
||||||
|
|
||||||
[testenv]
|
[testenv]
|
||||||
|
|
@ -16,6 +17,7 @@ deps =
|
||||||
django20: Django>=2.0,<2.1
|
django20: Django>=2.0,<2.1
|
||||||
|
|
||||||
drf37: djangorestframework>=3.7.7,<3.8
|
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
|
# 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
|
drfmaster: https://github.com/encode/django-rest-framework/archive/master.tar.gz
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue