Skip to content

Commit 7261ae6

Browse files
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
1 parent efb047f commit 7261ae6

File tree

2 files changed

+54
-5
lines changed

2 files changed

+54
-5
lines changed

rest_framework/schemas/generators.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -222,12 +222,11 @@ def get_allowed_methods(self, callback):
222222
if hasattr(callback, 'actions'):
223223
actions = set(callback.actions.keys())
224224
http_method_names = set(callback.cls.http_method_names)
225-
return [method.upper() for method in actions & http_method_names]
225+
methods = [method.upper() for method in actions & http_method_names]
226+
else:
227+
methods = callback.cls().allowed_methods
226228

227-
return [
228-
method for method in
229-
callback.cls().allowed_methods if method not in ('OPTIONS', 'HEAD')
230-
]
229+
return [method for method in methods if method not in ('OPTIONS', 'HEAD')]
231230

232231

233232
class SchemaGenerator(object):

tests/test_schemas.py

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -901,3 +901,53 @@ class TestView(generics.RetrieveAPIView):
901901

902902
is_list = is_list_view(path, method, view)
903903
assert not is_list, "RetrieveAPIView subclasses should not be classified as list views."
904+
905+
906+
def test_head_and_options_methods_are_excluded():
907+
"""
908+
Regression test for #5528
909+
https://github.com/encode/django-rest-framework/issues/5528
910+
911+
Viewset OPTIONS actions were not being correctly excluded
912+
913+
Initial cases here shown to be working as expected.
914+
"""
915+
916+
@api_view(['options', 'get'])
917+
def fbv(request):
918+
pass
919+
920+
inspector = EndpointEnumerator()
921+
922+
path = '/a/path/'
923+
callback = fbv
924+
925+
assert inspector.should_include_endpoint(path, callback)
926+
assert inspector.get_allowed_methods(callback) == ["GET"]
927+
928+
class AnAPIView(APIView):
929+
930+
def get(self, request, *args, **kwargs):
931+
pass
932+
933+
def options(self, request, *args, **kwargs):
934+
pass
935+
936+
callback = AnAPIView.as_view()
937+
938+
assert inspector.should_include_endpoint(path, callback)
939+
assert inspector.get_allowed_methods(callback) == ["GET"]
940+
941+
class AViewSet(ModelViewSet):
942+
943+
@detail_route(methods=['options', 'get'])
944+
def custom_action(self, request, pk):
945+
pass
946+
947+
callback = AViewSet.as_view({
948+
"options": "custom_action",
949+
"get": "custom_action"
950+
})
951+
952+
assert inspector.should_include_endpoint(path, callback)
953+
assert inspector.get_allowed_methods(callback) == ["GET"]

0 commit comments

Comments
 (0)