diff --git a/rest_framework/schemas/generators.py b/rest_framework/schemas/generators.py index 5fac352760..393e215756 100644 --- a/rest_framework/schemas/generators.py +++ b/rest_framework/schemas/generators.py @@ -222,12 +222,11 @@ def get_allowed_methods(self, callback): 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): diff --git a/tests/test_schemas.py b/tests/test_schemas.py index eec3060fb4..df49103013 100644 --- a/tests/test_schemas.py +++ b/tests/test_schemas.py @@ -901,3 +901,53 @@ class TestView(generics.RetrieveAPIView): 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"]
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies: