diff --git a/djangorestframework/tests/renderers.py b/djangorestframework/tests/renderers.py
index 77b5430fa..9a02d0a9a 100644
--- a/djangorestframework/tests/renderers.py
+++ b/djangorestframework/tests/renderers.py
@@ -177,6 +177,13 @@ _flat_repr = '{"foo": ["bar", "baz"]}'
_indented_repr = '{\n "foo": [\n "bar",\n "baz"\n ]\n}'
+def strip_trailing_whitespace(content):
+ """
+ Seems to be some inconsistencies re. trailing whitespace with
+ different versions of the json lib.
+ """
+ return re.sub(' +\n', '\n', content)
+
class JSONRendererTests(TestCase):
"""
Tests specific to the JSON Renderer
@@ -190,7 +197,6 @@ class JSONRendererTests(TestCase):
renderer = JSONRenderer(None)
content = renderer.render(obj, 'application/json')
# Fix failing test case which depends on version of JSON library.
- content = re.sub(' +\n', '\n', content)
self.assertEquals(content, _flat_repr)
def test_with_content_type_args(self):
@@ -200,7 +206,7 @@ class JSONRendererTests(TestCase):
obj = {'foo': ['bar', 'baz']}
renderer = JSONRenderer(None)
content = renderer.render(obj, 'application/json; indent=2')
- self.assertEquals(content, _indented_repr)
+ self.assertEquals(strip_trailing_whitespace(content), _indented_repr)
def test_render_and_parse(self):
"""
diff --git a/djangorestframework/views.py b/djangorestframework/views.py
index d89c2afd9..88d81d25b 100644
--- a/djangorestframework/views.py
+++ b/djangorestframework/views.py
@@ -27,6 +27,46 @@ __all__ = (
)
+def _remove_trailing_string(content, trailing):
+ """
+ Strip trailing component `trailing` from `content` if it exists.
+ Used when generating names from view/resource classes.
+ """
+ if content.endswith(trailing) and content != trailing:
+ return content[:-len(trailing)]
+ return content
+
+def _remove_leading_indent(content):
+ """
+ Remove leading indent from a block of text.
+ Used when generating descriptions from docstrings.
+ """
+ whitespace_counts = [len(line) - len(line.lstrip(' '))
+ for line in content.splitlines()[1:] if line.lstrip()]
+
+ # unindent the content if needed
+ if whitespace_counts:
+ whitespace_pattern = '^' + (' ' * min(whitespace_counts))
+ return re.sub(re.compile(whitespace_pattern, re.MULTILINE), '', content)
+ return content
+
+def _camelcase_to_spaces(content):
+ """
+ Translate 'CamelCaseNames' to 'Camel Case Names'.
+ Used when generating names from view/resource classes.
+ """
+ camelcase_boundry = '(((?<=[a-z])[A-Z])|([A-Z](?![A-Z]|$)))'
+ return re.sub(camelcase_boundry, ' \\1', content).strip()
+
+
+_resource_classes = (
+ None,
+ resources.Resource,
+ resources.FormResource,
+ resources.ModelResource
+)
+
+
class View(ResourceMixin, RequestMixin, ResponseMixin, AuthMixin, DjangoView):
"""
Handles incoming requests and maps them to REST operations.
@@ -84,52 +124,46 @@ class View(ResourceMixin, RequestMixin, ResponseMixin, AuthMixin, DjangoView):
Override to customize.
"""
# If this view has a resource that's been overridden, then use that resource for the name
- if getattr(self, 'resource', None) not in (None, resources.Resource, resources.FormResource, resources.ModelResource):
+ if getattr(self, 'resource', None) not in _resource_classes:
name = self.resource.__name__
+ name = _remove_trailing_string(name, 'Resource')
+ name += getattr(self, '_suffix', '')
- # Chomp of any non-descriptive trailing part of the resource class name
- if name.endswith('Resource') and name != 'Resource':
- name = name[:-len('Resource')]
-
- # If the view has a descriptive suffix, eg '*** List', '*** Instance'
- if getattr(self, '_suffix', None):
- name += self._suffix
# If it's a view class with no resource then grok the name from the class name
- elif getattr(self, '__class__', None) is not None:
- name = self.__class__.__name__
-
- # Chomp of any non-descriptive trailing part of the view class name
- if name.endswith('View') and name != 'View':
- name = name[:-len('View')]
else:
- name = ''
- return re.sub('(((?<=[a-z])[A-Z])|([A-Z](?![A-Z]|$)))', ' \\1', name).strip()
+ name = self.__class__.__name__
+ name = _remove_trailing_string(name, 'View')
+
+ return _camelcase_to_spaces(name)
def get_description(self, html=False):
"""
Return the resource or view docstring for use as this view's description.
Override to customize.
"""
- # If this view has a resource that's been overridden, then use the resource's doctring
- if getattr(self, 'resource', None) not in (None, resources.Resource, resources.FormResource, resources.ModelResource):
- doc = self.resource.__doc__
- # Otherwise use the view doctring
- elif getattr(self, '__doc__', None):
- doc = self.__doc__
+
+ description = None
+
+ # If this view has a resource that's been overridden,
+ # then try to use the resource's docstring
+ if getattr(self, 'resource', None) not in _resource_classes:
+ description = self.resource.__doc__
+
+ # Otherwise use the view docstring
+ if not description:
+ description = self.__doc__ or ''
+
+ description = _remove_leading_indent(description)
+
+ if html:
+ return self.markup_description(description)
+ return description
+
+ def markup_description(self, description):
+ if apply_markdown:
+ return apply_markdown(description)
else:
- doc = ''
- whitespace_counts = [len(line) - len(line.lstrip(' ')) for line in doc.splitlines()[1:] if line.lstrip()]
- # unindent the docstring if needed
- if whitespace_counts:
- whitespace_pattern = '^' + (' ' * min(whitespace_counts))
- doc = re.sub(re.compile(whitespace_pattern, re.MULTILINE), '', doc)
- if doc and html:
- if apply_markdown:
- doc = apply_markdown(doc)
- else:
- doc = escape(doc)
- doc = mark_safe(doc.replace('\n', '
'))
- return doc
+ return mark_safe(escape(description).replace('\n', '
'))
def http_method_not_allowed(self, request, *args, **kwargs):
"""