Schema: Exclude OPTIONS/HEAD for ViewSet actions (#5532)

Closes #5528.

Viewset custom actions (@detail_route etc) OPTIONS (and HEAD) methods were not being excluded from Schema Generations.

This PR adds a test reproducing the reported error and adjusts `EndpointEnumerator.get_allowed_methods()` to filter ViewSet actions in the same way as other `APIView`s
This commit is contained in:
Carlton Gibson 2017-10-25 10:56:40 +02:00 committed by GitHub
parent efb047fa07
commit 7261ae653a
2 changed files with 54 additions and 5 deletions

View File

@ -222,12 +222,11 @@ class EndpointEnumerator(object):
if hasattr(callback, 'actions'):
actions = set(callback.actions.keys())
http_method_names = set(callback.cls.http_method_names)
return [method.upper() for method in actions & http_method_names]
methods = [method.upper() for method in actions & http_method_names]
else:
methods = callback.cls().allowed_methods
return [
method for method in
callback.cls().allowed_methods if method not in ('OPTIONS', 'HEAD')
]
return [method for method in methods if method not in ('OPTIONS', 'HEAD')]
class SchemaGenerator(object):

View File

@ -901,3 +901,53 @@ def test_is_list_view_recognises_retrieve_view_subclasses():
is_list = is_list_view(path, method, view)
assert not is_list, "RetrieveAPIView subclasses should not be classified as list views."
def test_head_and_options_methods_are_excluded():
"""
Regression test for #5528
https://github.com/encode/django-rest-framework/issues/5528
Viewset OPTIONS actions were not being correctly excluded
Initial cases here shown to be working as expected.
"""
@api_view(['options', 'get'])
def fbv(request):
pass
inspector = EndpointEnumerator()
path = '/a/path/'
callback = fbv
assert inspector.should_include_endpoint(path, callback)
assert inspector.get_allowed_methods(callback) == ["GET"]
class AnAPIView(APIView):
def get(self, request, *args, **kwargs):
pass
def options(self, request, *args, **kwargs):
pass
callback = AnAPIView.as_view()
assert inspector.should_include_endpoint(path, callback)
assert inspector.get_allowed_methods(callback) == ["GET"]
class AViewSet(ModelViewSet):
@detail_route(methods=['options', 'get'])
def custom_action(self, request, pk):
pass
callback = AViewSet.as_view({
"options": "custom_action",
"get": "custom_action"
})
assert inspector.should_include_endpoint(path, callback)
assert inspector.get_allowed_methods(callback) == ["GET"]