Skip to content

Commit df58435

Browse files
authored
Prevent head method mapping to coerce action name (#7729)
1 parent 791d48c commit df58435

File tree

2 files changed

+66
-1
lines changed

2 files changed

+66
-1
lines changed

rest_framework/schemas/coreapi.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,11 @@ def get_keys(self, subpath, method, view):
198198

199199
if is_custom_action(action):
200200
# Custom action, eg "/users/{pk}/activate/", "/users/active/"
201-
if len(view.action_map) > 1:
201+
mapped_methods = {
202+
# Don't count head mapping, e.g. not part of the schema
203+
method for method in view.action_map if method != 'head'
204+
}
205+
if len(mapped_methods) > 1:
202206
action = self.default_mapping[method.lower()]
203207
if action in self.coerce_method_names:
204208
action = self.coerce_method_names[action]

tests/schemas/test_coreapi.py

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -754,6 +754,67 @@ def test_schema_for_regular_views(self):
754754
assert schema == expected
755755

756756

757+
@unittest.skipUnless(coreapi, 'coreapi is not installed')
758+
@override_settings(REST_FRAMEWORK={'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.AutoSchema'})
759+
class TestSchemaGeneratorActionKeysViewSets(TestCase):
760+
def test_action_not_coerced_for_get_and_head(self):
761+
"""
762+
Ensure that action name is preserved when action map contains "head".
763+
"""
764+
class CustomViewSet(GenericViewSet):
765+
serializer_class = EmptySerializer
766+
767+
@action(methods=['get', 'head'], detail=True)
768+
def custom_read(self, request, pk):
769+
raise NotImplementedError
770+
771+
@action(methods=['put', 'patch'], detail=True)
772+
def custom_mixed_update(self, request, pk):
773+
raise NotImplementedError
774+
775+
self.router = DefaultRouter()
776+
self.router.register('example', CustomViewSet, basename='example')
777+
self.patterns = [
778+
path('', include(self.router.urls))
779+
]
780+
781+
generator = SchemaGenerator(title='Example API', patterns=self.patterns)
782+
schema = generator.get_schema()
783+
784+
expected = coreapi.Document(
785+
url='',
786+
title='Example API',
787+
content={
788+
'example': {
789+
'custom_read': coreapi.Link(
790+
url='/example/{id}/custom_read/',
791+
action='get',
792+
fields=[
793+
coreapi.Field('id', required=True, location='path', schema=coreschema.String()),
794+
]
795+
),
796+
'custom_mixed_update': {
797+
'update': coreapi.Link(
798+
url='/example/{id}/custom_mixed_update/',
799+
action='put',
800+
fields=[
801+
coreapi.Field('id', required=True, location='path', schema=coreschema.String()),
802+
]
803+
),
804+
'partial_update': coreapi.Link(
805+
url='/example/{id}/custom_mixed_update/',
806+
action='patch',
807+
fields=[
808+
coreapi.Field('id', required=True, location='path', schema=coreschema.String()),
809+
]
810+
)
811+
}
812+
}
813+
}
814+
)
815+
assert schema == expected
816+
817+
757818
@unittest.skipUnless(coreapi, 'coreapi is not installed')
758819
@override_settings(REST_FRAMEWORK={'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.AutoSchema'})
759820
class Test4605Regression(TestCase):

0 commit comments

Comments
 (0)