parent
4c069138e8
commit
65aac1da2c
|
|
@ -117,19 +117,22 @@ 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 an @action, it will have a bind_to_methods attribute, or a mapper attribute for drf>3.8
|
# if the method is an @action, it will have a bind_to_methods attribute, or a mapping attribute for drf>3.8
|
||||||
bind_to_methods = getattr(view_method, 'bind_to_methods', [])
|
bind_to_methods = getattr(view_method, 'bind_to_methods', [])
|
||||||
|
mapping = getattr(view_method, 'mapping', {})
|
||||||
|
mapping_methods = [mth for mth, name in mapping.items() if name == view_method.__name__]
|
||||||
|
action_http_methods = bind_to_methods + mapping_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)
|
||||||
http_method_names = [m for m in getattr(view_cls, 'http_method_names', []) if hasattr(view_cls, m)]
|
api_view_http_methods = [m for m in getattr(view_cls, 'http_method_names', []) if hasattr(view_cls, m)]
|
||||||
|
|
||||||
available_methods = http_method_names + bind_to_methods
|
available_http_methods = api_view_http_methods + action_http_methods
|
||||||
existing_data = getattr(view_method, '_swagger_auto_schema', {})
|
existing_data = getattr(view_method, '_swagger_auto_schema', {})
|
||||||
|
|
||||||
_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_http_methods, "`method` or `methods` can only be specified on @action 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"
|
||||||
|
|
@ -137,20 +140,20 @@ def swagger_auto_schema(method=None, methods=None, auto_schema=unset, request_bo
|
||||||
_methods = [method.lower()]
|
_methods = [method.lower()]
|
||||||
else:
|
else:
|
||||||
_methods = [mth.lower() for mth in methods]
|
_methods = [mth.lower() for mth in methods]
|
||||||
assert all(mth in available_methods for mth in _methods), "http method not bound to view"
|
assert all(mth in available_http_methods for mth in _methods), "http method not bound to view"
|
||||||
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_http_methods:
|
||||||
# action or api_view
|
# action or api_view
|
||||||
assert bool(http_method_names) != bool(bind_to_methods), "this should never happen"
|
assert bool(api_view_http_methods) != bool(action_http_methods), "this should never happen"
|
||||||
|
|
||||||
if len(available_methods) > 1:
|
if len(available_http_methods) > 1:
|
||||||
assert _methods, \
|
assert _methods, \
|
||||||
"on multi-method api_view, action, detail_route or list_route, you must specify " \
|
"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"
|
"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_http_methods
|
||||||
|
|
||||||
assert not any(hasattr(getattr(view_cls, mth, None), '_swagger_auto_schema') for mth in _methods), \
|
assert not any(hasattr(getattr(view_cls, mth, None), '_swagger_auto_schema') for mth in _methods), \
|
||||||
"swagger_auto_schema applied twice to method"
|
"swagger_auto_schema applied twice to method"
|
||||||
|
|
|
||||||
|
|
@ -147,3 +147,46 @@ def test_url_order():
|
||||||
|
|
||||||
# get_endpoints only includes one endpoint
|
# get_endpoints only includes one endpoint
|
||||||
assert len(generator.get_endpoints(None)['/test/'][1]) == 1
|
assert len(generator.get_endpoints(None)['/test/'][1]) == 1
|
||||||
|
|
||||||
|
|
||||||
|
try:
|
||||||
|
from rest_framework.decorators import action, MethodMapper
|
||||||
|
except ImportError:
|
||||||
|
action = MethodMapper = None
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.skipif(not MethodMapper or not action, reason="action.mapping test (djangorestframework>=3.9 required)")
|
||||||
|
def test_action_mapping():
|
||||||
|
class ActionViewSet(viewsets.ViewSet):
|
||||||
|
@swagger_auto_schema(method='get', operation_id='mapping_get')
|
||||||
|
@swagger_auto_schema(method='delete', operation_id='mapping_delete')
|
||||||
|
@action(detail=False, methods=['get', 'delete'], url_path='test')
|
||||||
|
def action_main(self, request):
|
||||||
|
"""mapping docstring get/delete"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
@swagger_auto_schema(operation_id='mapping_post')
|
||||||
|
@action_main.mapping.post
|
||||||
|
def action_post(self, request):
|
||||||
|
"""mapping docstring post"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
router = routers.DefaultRouter()
|
||||||
|
router.register(r'action', ActionViewSet, base_name='action')
|
||||||
|
|
||||||
|
generator = OpenAPISchemaGenerator(
|
||||||
|
info=openapi.Info(title="Test generator", default_version="v1"),
|
||||||
|
version="v2",
|
||||||
|
url='',
|
||||||
|
patterns=router.urls
|
||||||
|
)
|
||||||
|
|
||||||
|
for _ in range(3):
|
||||||
|
swagger = generator.get_schema(None, True)
|
||||||
|
action_ops = swagger['paths']['/test/']
|
||||||
|
methods = ['get', 'post', 'delete']
|
||||||
|
assert all(mth in action_ops for mth in methods)
|
||||||
|
assert all(action_ops[mth]['operationId'] == 'mapping_' + mth for mth in methods)
|
||||||
|
assert action_ops['post']['description'] == 'mapping docstring post'
|
||||||
|
assert action_ops['get']['description'] == 'mapping docstring get/delete'
|
||||||
|
assert action_ops['delete']['description'] == 'mapping docstring get/delete'
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue