From 5feb835929214b2d656f64d5aea6f169e2df59d7 Mon Sep 17 00:00:00 2001 From: Daniel Bate Date: Wed, 10 Oct 2018 09:29:29 +0100 Subject: [PATCH] Feature/action docs sections (#6060) * added ability to add sections to custom action documentation * added tests to cover docs sections in custom actions * added method specific docs test for action mapping * added docs for custom action documentation --- docs/topics/documenting-your-api.md | 22 +++++++++++ rest_framework/schemas/inspectors.py | 7 ++-- tests/test_schemas.py | 59 ++++++++++++++++++++++++++++ 3 files changed, 85 insertions(+), 3 deletions(-) diff --git a/docs/topics/documenting-your-api.md b/docs/topics/documenting-your-api.md index f57b51ee01..778ebb0424 100644 --- a/docs/topics/documenting-your-api.md +++ b/docs/topics/documenting-your-api.md @@ -90,6 +90,28 @@ When using viewsets, you should use the relevant action names as delimiters. Create a new user instance. """ +Custom actions on viewsets can also be documented in a similar way using the method names +as delimiters or by attaching the documentation to action mapping methods. + + class UserViewSet(viewsets.ModelViewset): + ... + + @action(detail=False, methods=['get', 'post']) + def some_action(self, request, *args, **kwargs): + """ + get: + A description of the get method on the custom action. + + post: + A description of the post method on the custom action. + """ + + @some_action.mapping.put + def put_some_action(): + """ + A description of the put method on the custom action. + """ + ### `documentation` API Reference diff --git a/rest_framework/schemas/inspectors.py b/rest_framework/schemas/inspectors.py index b90f60e084..02a38f3511 100644 --- a/rest_framework/schemas/inspectors.py +++ b/rest_framework/schemas/inspectors.py @@ -247,9 +247,11 @@ def get_description(self, path, method): method_docstring = getattr(view, method_name, None).__doc__ if method_docstring: # An explicit docstring on the method or action. - return formatting.dedent(smart_text(method_docstring)) + return self._get_description_section(view, method.lower(), formatting.dedent(smart_text(method_docstring))) + else: + return self._get_description_section(view, getattr(view, 'action', method.lower()), view.get_view_description()) - description = view.get_view_description() + def _get_description_section(self, view, header, description): lines = [line for line in description.splitlines()] current_section = '' sections = {'': ''} @@ -263,7 +265,6 @@ def get_description(self, path, method): # TODO: SCHEMA_COERCE_METHOD_NAMES appears here and in `SchemaGenerator.get_keys` coerce_method_names = api_settings.SCHEMA_COERCE_METHOD_NAMES - header = getattr(view, 'action', method.lower()) if header in sections: return sections[header].strip() if header in coerce_method_names: diff --git a/tests/test_schemas.py b/tests/test_schemas.py index 6d7091da2a..ad2e34a4ba 100644 --- a/tests/test_schemas.py +++ b/tests/test_schemas.py @@ -114,6 +114,24 @@ def get_serializer(self, *args, **kwargs): assert self.action return super(ExampleViewSet, self).get_serializer(*args, **kwargs) + @action(methods=['get', 'post'], detail=False) + def documented_custom_action(self, request): + """ + get: + A description of the get method on the custom action. + + post: + A description of the post method on the custom action. + """ + pass + + @documented_custom_action.mapping.put + def put_documented_custom_action(self, request, *args, **kwargs): + """ + A description of the put method on the custom action from mapping. + """ + pass + if coreapi: schema_view = get_schema_view(title='Example API') @@ -161,6 +179,13 @@ def test_anonymous_request(self): description='Custom description.', ) }, + 'documented_custom_action': { + 'read': coreapi.Link( + url='/example/documented_custom_action/', + action='get', + description='A description of the get method on the custom action.', + ) + }, 'read': coreapi.Link( url='/example/{id}/', action='get', @@ -263,6 +288,33 @@ def test_authenticated_request(self): description='Deletion description.', ), }, + 'documented_custom_action': { + 'read': coreapi.Link( + url='/example/documented_custom_action/', + action='get', + description='A description of the get method on the custom action.', + ), + 'create': coreapi.Link( + url='/example/documented_custom_action/', + action='post', + description='A description of the post method on the custom action.', + encoding='application/json', + fields=[ + coreapi.Field('a', required=True, location='form', schema=coreschema.String(title='A', description='A field description')), + coreapi.Field('b', required=False, location='form', schema=coreschema.String(title='B')) + ] + ), + 'update': coreapi.Link( + url='/example/documented_custom_action/', + action='put', + description='A description of the put method on the custom action from mapping.', + encoding='application/json', + fields=[ + coreapi.Field('a', required=True, location='form', schema=coreschema.String(title='A', description='A field description')), + coreapi.Field('b', required=False, location='form', schema=coreschema.String(title='B')) + ] + ), + }, 'update': coreapi.Link( url='/example/{id}/', action='put', @@ -548,6 +600,13 @@ def test_schema_for_regular_views(self): description='Custom description.', ) }, + 'documented_custom_action': { + 'read': coreapi.Link( + url='/example1/documented_custom_action/', + action='get', + description='A description of the get method on the custom action.', + ), + }, 'read': coreapi.Link( url='/example1/{id}/', action='get',