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-build.properties
|
||||
fabric.properties
|
||||
|
||||
\.pytest_cache/
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/testproj" isTestSource="false" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/docs/_build" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/venv" />
|
||||
</content>
|
||||
<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">
|
||||
<option name="ourVersions">
|
||||
<value>
|
||||
<list size="4">
|
||||
<list size="5">
|
||||
<item index="0" class="java.lang.String" itemvalue="2.7" />
|
||||
<item index="1" class="java.lang.String" itemvalue="3.4" />
|
||||
<item index="2" class="java.lang.String" itemvalue="3.5" />
|
||||
<item index="3" class="java.lang.String" itemvalue="3.6" />
|
||||
<item index="4" class="java.lang.String" itemvalue="3.7" />
|
||||
</list>
|
||||
</value>
|
||||
</option>
|
||||
|
|
|
|||
|
|
@ -70,4 +70,7 @@
|
|||
</LinkMapSettings>
|
||||
</component>
|
||||
<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>
|
||||
|
|
@ -9,6 +9,7 @@ python:
|
|||
|
||||
env:
|
||||
- DRF=3.7
|
||||
- DRF=3.8
|
||||
|
||||
jobs:
|
||||
include:
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
...
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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"""
|
||||
|
|
|
|||
4
tox.ini
4
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
|
||||
|
|
|
|||
Loading…
Reference in New Issue