diff options
| -rw-r--r-- | rest_framework/renderers.py | 5 | ||||
| -rw-r--r-- | rest_framework/settings.py | 6 | ||||
| -rw-r--r-- | rest_framework/tests/test_description.py | 9 | ||||
| -rw-r--r-- | rest_framework/utils/breadcrumbs.py | 5 | ||||
| -rw-r--r-- | rest_framework/utils/formatting.py | 38 | ||||
| -rw-r--r-- | rest_framework/views.py | 27 |
6 files changed, 50 insertions, 40 deletions
diff --git a/rest_framework/renderers.py b/rest_framework/renderers.py index 3a03ca33..1006e26c 100644 --- a/rest_framework/renderers.py +++ b/rest_framework/renderers.py @@ -24,7 +24,6 @@ from rest_framework.settings import api_settings from rest_framework.request import clone_request from rest_framework.utils import encoders from rest_framework.utils.breadcrumbs import get_breadcrumbs -from rest_framework.utils.formatting import get_view_name, get_view_description from rest_framework import exceptions, parsers, status, VERSION @@ -498,10 +497,10 @@ class BrowsableAPIRenderer(BaseRenderer): return GenericContentForm() def get_name(self, view): - return get_view_name(view.__class__, getattr(view, 'suffix', None)) + return view.get_view_name() def get_description(self, view): - return get_view_description(view.__class__, html=True) + return view.get_view_description(html=True) def get_breadcrumbs(self, request): return get_breadcrumbs(request.path) diff --git a/rest_framework/settings.py b/rest_framework/settings.py index 8fd177d5..0b2bdb62 100644 --- a/rest_framework/settings.py +++ b/rest_framework/settings.py @@ -69,6 +69,10 @@ DEFAULTS = { 'PAGINATE_BY': None, 'PAGINATE_BY_PARAM': None, + # View configuration + 'VIEW_NAME_FUNCTION': 'rest_framework.utils.formatting.view_name', + 'VIEW_DESCRIPTION_FUNCTION': 'rest_framework.utils.formatting.view_description', + # Authentication 'UNAUTHENTICATED_USER': 'django.contrib.auth.models.AnonymousUser', 'UNAUTHENTICATED_TOKEN': None, @@ -125,6 +129,8 @@ IMPORT_STRINGS = ( 'TEST_REQUEST_RENDERER_CLASSES', 'UNAUTHENTICATED_USER', 'UNAUTHENTICATED_TOKEN', + 'VIEW_NAME_FUNCTION', + 'VIEW_DESCRIPTION_FUNCTION' ) diff --git a/rest_framework/tests/test_description.py b/rest_framework/tests/test_description.py index 8019f5ec..4c03c1de 100644 --- a/rest_framework/tests/test_description.py +++ b/rest_framework/tests/test_description.py @@ -6,7 +6,6 @@ from rest_framework.compat import apply_markdown, smart_text from rest_framework.views import APIView from rest_framework.tests.description import ViewWithNonASCIICharactersInDocstring from rest_framework.tests.description import UTF8_TEST_DOCSTRING -from rest_framework.utils.formatting import get_view_name, get_view_description # We check that docstrings get nicely un-indented. DESCRIPTION = """an example docstring @@ -58,7 +57,7 @@ class TestViewNamesAndDescriptions(TestCase): """ class MockView(APIView): pass - self.assertEqual(get_view_name(MockView), 'Mock') + self.assertEqual(MockView().get_view_name(), 'Mock') def test_view_description_uses_docstring(self): """Ensure view descriptions are based on the docstring.""" @@ -78,7 +77,7 @@ class TestViewNamesAndDescriptions(TestCase): # hash style header #""" - self.assertEqual(get_view_description(MockView), DESCRIPTION) + self.assertEqual(MockView().get_view_description(), DESCRIPTION) def test_view_description_supports_unicode(self): """ @@ -86,7 +85,7 @@ class TestViewNamesAndDescriptions(TestCase): """ self.assertEqual( - get_view_description(ViewWithNonASCIICharactersInDocstring), + ViewWithNonASCIICharactersInDocstring().get_view_description(), smart_text(UTF8_TEST_DOCSTRING) ) @@ -97,7 +96,7 @@ class TestViewNamesAndDescriptions(TestCase): """ class MockView(APIView): pass - self.assertEqual(get_view_description(MockView), '') + self.assertEqual(MockView().get_view_description(), '') def test_markdown(self): """ diff --git a/rest_framework/utils/breadcrumbs.py b/rest_framework/utils/breadcrumbs.py index d51374b0..0384faba 100644 --- a/rest_framework/utils/breadcrumbs.py +++ b/rest_framework/utils/breadcrumbs.py @@ -1,6 +1,5 @@ from __future__ import unicode_literals from django.core.urlresolvers import resolve, get_script_prefix -from rest_framework.utils.formatting import get_view_name def get_breadcrumbs(url): @@ -29,8 +28,8 @@ def get_breadcrumbs(url): # Don't list the same view twice in a row. # Probably an optional trailing slash. if not seen or seen[-1] != view: - suffix = getattr(view, 'suffix', None) - name = get_view_name(view.cls, suffix) + instance = view.cls() + name = instance.get_view_name() breadcrumbs_list.insert(0, (name, prefix + url)) seen.append(view) diff --git a/rest_framework/utils/formatting.py b/rest_framework/utils/formatting.py index 4bec8387..89a89252 100644 --- a/rest_framework/utils/formatting.py +++ b/rest_framework/utils/formatting.py @@ -7,6 +7,7 @@ from django.utils.html import escape from django.utils.safestring import mark_safe from rest_framework.compat import apply_markdown, smart_text import re +from rest_framework.settings import api_settings def _remove_trailing_string(content, trailing): @@ -44,37 +45,30 @@ def _camelcase_to_spaces(content): content = re.sub(camelcase_boundry, ' \\1', content).strip() return ' '.join(content.split('_')).title() - -def get_view_name(cls, suffix=None): +def markup_description(description): """ - Return a formatted name for an `APIView` class or `@api_view` function. + Apply HTML markup to the given description. """ - name = cls.__name__ + if apply_markdown: + description = apply_markdown(description) + else: + description = escape(description).replace('\n', '<br />') + return mark_safe(description) + + +def view_name(instance, view, suffix=None): + name = view.__name__ name = _remove_trailing_string(name, 'View') name = _remove_trailing_string(name, 'ViewSet') name = _camelcase_to_spaces(name) if suffix: name += ' ' + suffix - return name + return name -def get_view_description(cls, html=False): - """ - Return a description for an `APIView` class or `@api_view` function. - """ - description = cls.__doc__ or '' +def view_description(instance, view, html=False): + description = view.__doc__ or '' description = _remove_leading_indent(smart_text(description)) if html: return markup_description(description) - return description - - -def markup_description(description): - """ - Apply HTML markup to the given description. - """ - if apply_markdown: - description = apply_markdown(description) - else: - description = escape(description).replace('\n', '<br />') - return mark_safe(description) + return description
\ No newline at end of file diff --git a/rest_framework/views.py b/rest_framework/views.py index d51233a9..4553714a 100644 --- a/rest_framework/views.py +++ b/rest_framework/views.py @@ -12,7 +12,6 @@ from rest_framework.compat import View, HttpResponseBase from rest_framework.request import Request from rest_framework.response import Response from rest_framework.settings import api_settings -from rest_framework.utils.formatting import get_view_name, get_view_description class APIView(View): @@ -25,6 +24,9 @@ class APIView(View): permission_classes = api_settings.DEFAULT_PERMISSION_CLASSES content_negotiation_class = api_settings.DEFAULT_CONTENT_NEGOTIATION_CLASS + view_name_function = api_settings.VIEW_NAME_FUNCTION + view_description_function = api_settings.VIEW_DESCRIPTION_FUNCTION + @classmethod def as_view(cls, **initkwargs): """ @@ -157,6 +159,21 @@ class APIView(View): self._negotiator = self.content_negotiation_class() return self._negotiator + def get_view_name(self): + """ + Get the view name + """ + # This is used by ViewSets to disambiguate instance vs list views + view_name_suffix = getattr(self, 'suffix', None) + + return self.view_name_function(self.__class__, view_name_suffix) + + def get_view_description(self, html=False): + """ + Get the view description + """ + return self.view_description_function(self.__class__, html) + # API policy implementation methods def perform_content_negotiation(self, request, force=False): @@ -342,16 +359,12 @@ class APIView(View): Return a dictionary of metadata about the view. Used to return responses for OPTIONS requests. """ - - # This is used by ViewSets to disambiguate instance vs list views - view_name_suffix = getattr(self, 'suffix', None) - # By default we can't provide any form-like information, however the # generic views override this implementation and add additional # information for POST and PUT methods, based on the serializer. ret = SortedDict() - ret['name'] = get_view_name(self.__class__, view_name_suffix) - ret['description'] = get_view_description(self.__class__) + ret['name'] = self.get_view_name() + ret['description'] = self.get_view_description() ret['renders'] = [renderer.media_type for renderer in self.renderer_classes] ret['parses'] = [parser.media_type for parser in self.parser_classes] return ret |
