diff --git a/rest_framework/schemas/coreapi.py b/rest_framework/schemas/coreapi.py index 179f0fa3c..908518e9d 100644 --- a/rest_framework/schemas/coreapi.py +++ b/rest_framework/schemas/coreapi.py @@ -198,7 +198,11 @@ class SchemaGenerator(BaseSchemaGenerator): if is_custom_action(action): # Custom action, eg "/users/{pk}/activate/", "/users/active/" - if len(view.action_map) > 1: + mapped_methods = { + # Don't count head mapping, e.g. not part of the schema + method for method in view.action_map if method != 'head' + } + if len(mapped_methods) > 1: action = self.default_mapping[method.lower()] if action in self.coerce_method_names: action = self.coerce_method_names[action] diff --git a/tests/schemas/test_coreapi.py b/tests/schemas/test_coreapi.py index 7b1f15fef..eddc5243e 100644 --- a/tests/schemas/test_coreapi.py +++ b/tests/schemas/test_coreapi.py @@ -754,6 +754,67 @@ class TestSchemaGeneratorWithManyToMany(TestCase): assert schema == expected +@unittest.skipUnless(coreapi, 'coreapi is not installed') +@override_settings(REST_FRAMEWORK={'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.AutoSchema'}) +class TestSchemaGeneratorActionKeysViewSets(TestCase): + def test_action_not_coerced_for_get_and_head(self): + """ + Ensure that action name is preserved when action map contains "head". + """ + class CustomViewSet(GenericViewSet): + serializer_class = EmptySerializer + + @action(methods=['get', 'head'], detail=True) + def custom_read(self, request, pk): + raise NotImplementedError + + @action(methods=['put', 'patch'], detail=True) + def custom_mixed_update(self, request, pk): + raise NotImplementedError + + self.router = DefaultRouter() + self.router.register('example', CustomViewSet, basename='example') + self.patterns = [ + path('', include(self.router.urls)) + ] + + generator = SchemaGenerator(title='Example API', patterns=self.patterns) + schema = generator.get_schema() + + expected = coreapi.Document( + url='', + title='Example API', + content={ + 'example': { + 'custom_read': coreapi.Link( + url='/example/{id}/custom_read/', + action='get', + fields=[ + coreapi.Field('id', required=True, location='path', schema=coreschema.String()), + ] + ), + 'custom_mixed_update': { + 'update': coreapi.Link( + url='/example/{id}/custom_mixed_update/', + action='put', + fields=[ + coreapi.Field('id', required=True, location='path', schema=coreschema.String()), + ] + ), + 'partial_update': coreapi.Link( + url='/example/{id}/custom_mixed_update/', + action='patch', + fields=[ + coreapi.Field('id', required=True, location='path', schema=coreschema.String()), + ] + ) + } + } + } + ) + assert schema == expected + + @unittest.skipUnless(coreapi, 'coreapi is not installed') @override_settings(REST_FRAMEWORK={'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.AutoSchema'}) class Test4605Regression(TestCase):