{{ name }}
-{% if markeddown %}{% autoescape off %}{{ markeddown }}{% endautoescape %}{% else %}{{ description|linebreaksbr }}{% endif %}
+{% autoescape off %}{{ description }}{% endautoescape %}
{{ response.status }} {{ response.status_text }}{% autoescape off %}
{% for key, val in response.headers.items %}{{ key }}: {{ val|urlize_quoted_links }}
diff --git a/djangorestframework/tests/description.py b/djangorestframework/tests/description.py
index 56dcdfab..212b4ca4 100644
--- a/djangorestframework/tests/description.py
+++ b/djangorestframework/tests/description.py
@@ -1,7 +1,6 @@
from django.test import TestCase
from djangorestframework.views import View
from djangorestframework.compat import apply_markdown
-from djangorestframework.utils.description import get_name, get_description
# We check that docstrings get nicely un-indented.
DESCRIPTION = """an example docstring
@@ -51,15 +50,15 @@ class TestViewNamesAndDescriptions(TestCase):
"""Ensure Resource names are based on the classname by default."""
class MockView(View):
pass
- self.assertEquals(get_name(MockView()), 'Mock')
+ self.assertEquals(MockView().get_name(), 'Mock')
- # This has been turned off now.
- #def test_resource_name_can_be_set_explicitly(self):
- # """Ensure Resource names can be set using the 'name' class attribute."""
- # example = 'Some Other Name'
- # class MockView(View):
- # name = example
- # self.assertEquals(get_name(MockView()), example)
+ def test_resource_name_can_be_set_explicitly(self):
+ """Ensure Resource names can be set using the 'get_name' method."""
+ example = 'Some Other Name'
+ class MockView(View):
+ def get_name(self):
+ return example
+ self.assertEquals(MockView().get_name(), example)
def test_resource_description_uses_docstring_by_default(self):
"""Ensure Resource names are based on the docstring by default."""
@@ -79,29 +78,30 @@ class TestViewNamesAndDescriptions(TestCase):
# hash style header #"""
- self.assertEquals(get_description(MockView()), DESCRIPTION)
-
- # This has been turned off now
- #def test_resource_description_can_be_set_explicitly(self):
- # """Ensure Resource descriptions can be set using the 'description' class attribute."""
- # example = 'Some other description'
- # class MockView(View):
- # """docstring"""
- # description = example
- # self.assertEquals(get_description(MockView()), example)
+ self.assertEquals(MockView().get_description(), DESCRIPTION)
- #def test_resource_description_does_not_require_docstring(self):
- # """Ensure that empty docstrings do not affect the Resource's description if it has been set using the 'description' class attribute."""
- # example = 'Some other description'
- # class MockView(View):
- # description = example
- # self.assertEquals(get_description(MockView()), example)
+ def test_resource_description_can_be_set_explicitly(self):
+ """Ensure Resource descriptions can be set using the 'get_description' method."""
+ example = 'Some other description'
+ class MockView(View):
+ """docstring"""
+ def get_description(self):
+ return example
+ self.assertEquals(MockView().get_description(), example)
+
+ def test_resource_description_does_not_require_docstring(self):
+ """Ensure that empty docstrings do not affect the Resource's description if it has been set using the 'get_description' method."""
+ example = 'Some other description'
+ class MockView(View):
+ def get_description(self):
+ return example
+ self.assertEquals(MockView().get_description(), example)
def test_resource_description_can_be_empty(self):
- """Ensure that if a resource has no doctring or 'description' class attribute, then it's description is the empty string"""
+ """Ensure that if a resource has no doctring or 'description' class attribute, then it's description is the empty string."""
class MockView(View):
pass
- self.assertEquals(get_description(MockView()), '')
+ self.assertEquals(MockView().get_description(), '')
def test_markdown(self):
"""Ensure markdown to HTML works as expected"""
diff --git a/djangorestframework/utils/breadcrumbs.py b/djangorestframework/utils/breadcrumbs.py
index cfc63a47..85e13a5a 100644
--- a/djangorestframework/utils/breadcrumbs.py
+++ b/djangorestframework/utils/breadcrumbs.py
@@ -1,6 +1,4 @@
from django.core.urlresolvers import resolve
-from djangorestframework.utils.description import get_name
-
def get_breadcrumbs(url):
"""Given a url returns a list of breadcrumbs, which are each a tuple of (name, url)."""
@@ -17,7 +15,7 @@ def get_breadcrumbs(url):
else:
# Check if this is a REST framework view, and if so add it to the breadcrumbs
if isinstance(getattr(view, 'cls_instance', None), View):
- breadcrumbs_list.insert(0, (get_name(view), url))
+ breadcrumbs_list.insert(0, (view.cls_instance.get_name(), url))
if url == '':
# All done
diff --git a/djangorestframework/utils/description.py b/djangorestframework/utils/description.py
deleted file mode 100644
index 096cf57f..00000000
--- a/djangorestframework/utils/description.py
+++ /dev/null
@@ -1,88 +0,0 @@
-"""
-Get a descriptive name and description for a view.
-"""
-import re
-from djangorestframework.resources import Resource, FormResource, ModelResource
-
-
-# These a a bit Grungy, but they do the job.
-
-def get_name(view):
- """
- Return a name for the view.
-
- If view has a name attribute, use that, otherwise use the view's class name, with 'CamelCaseNames' converted to 'Camel Case Names'.
- """
-
- # If we're looking up the name of a view callable, as found by reverse,
- # grok the class instance that we stored when as_view was called.
- if getattr(view, 'cls_instance', None):
- view = view.cls_instance
-
- # If this view has a resource that's been overridden, then use that resource for the name
- if getattr(view, 'resource', None) not in (None, Resource, FormResource, ModelResource):
- name = view.resource.__name__
-
- # 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(view, '_suffix', None):
- name += view._suffix
-
- # Otherwise if it's a function view use the function's name
- elif getattr(view, '__name__', None) is not None:
- name = view.__name__
-
- # If it's a view class with no resource then grok the name from the class name
- elif getattr(view, '__class__', None) is not None:
- name = view.__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')]
-
- # I ain't got nuthin fo' ya
- else:
- return ''
-
- return re.sub('(((?<=[a-z])[A-Z])|([A-Z](?![A-Z]|$)))', ' \\1', name).strip()
-
-
-def get_description(view):
- """
- Provide a description for the view.
-
- By default this is the view's docstring with nice unindention applied.
- """
-
- # If we're looking up the name of a view callable, as found by reverse,
- # grok the class instance that we stored when as_view was called.
- if getattr(view, 'cls_instance', None):
- view = view.cls_instance
-
- # If this view has a resource that's been overridden, then use the resource's doctring
- if getattr(view, 'resource', None) not in (None, Resource, FormResource, ModelResource):
- doc = view.resource.__doc__
-
- # Otherwise use the view doctring
- elif getattr(view, '__doc__', None):
- doc = view.__doc__
-
- # I ain't got nuthin fo' ya
- else:
- return ''
-
- if not doc:
- return ''
-
- 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))
- return re.sub(re.compile(whitespace_pattern, re.MULTILINE), '', doc)
-
- # otherwise return it as-is
- return doc
diff --git a/djangorestframework/views.py b/djangorestframework/views.py
index 9f53868b..9dd84bf2 100644
--- a/djangorestframework/views.py
+++ b/djangorestframework/views.py
@@ -5,15 +5,17 @@ be subclassing in your implementation.
By setting or modifying class attributes on your view, you change it's predefined behaviour.
"""
+import re
from django.core.urlresolvers import set_script_prefix, get_script_prefix
from django.http import HttpResponse
+from django.utils.html import escape
+from django.utils.safestring import mark_safe
from django.views.decorators.csrf import csrf_exempt
-from djangorestframework.compat import View as DjangoView
+from djangorestframework.compat import View as DjangoView, apply_markdown
from djangorestframework.response import Response, ErrorResponse
from djangorestframework.mixins import *
from djangorestframework import resources, renderers, parsers, authentication, permissions, status
-from djangorestframework.utils.description import get_name, get_description
__all__ = (
@@ -25,6 +27,7 @@ __all__ = (
)
+
class View(ResourceMixin, RequestMixin, ResponseMixin, AuthMixin, DjangoView):
"""
Handles incoming requests and maps them to REST operations.
@@ -47,13 +50,13 @@ class View(ResourceMixin, RequestMixin, ResponseMixin, AuthMixin, DjangoView):
List of parsers the resource can parse the request with.
"""
- authentication = (authentication.UserLoggedInAuthentication,
- authentication.BasicAuthentication)
+ authentication = ( authentication.UserLoggedInAuthentication,
+ authentication.BasicAuthentication )
"""
List of all authenticating methods to attempt.
"""
- permissions = (permissions.FullAnonAccess,)
+ permissions = ( permissions.FullAnonAccess, )
"""
List of all permissions that must be checked.
"""
@@ -76,6 +79,59 @@ class View(ResourceMixin, RequestMixin, ResponseMixin, AuthMixin, DjangoView):
"""
return [method.upper() for method in self.http_method_names if hasattr(self, method)]
+ def get_name(self):
+ """
+ Return the resource or view class name for use as this view's name.
+ 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):
+ name = self.resource.__name__
+
+ # 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()
+
+ 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__
+ 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
+
def http_method_not_allowed(self, request, *args, **kwargs):
"""
Return an HTTP 405 error if an operation is called which does not have a handler method.
@@ -161,8 +217,8 @@ class View(ResourceMixin, RequestMixin, ResponseMixin, AuthMixin, DjangoView):
def options(self, request, *args, **kwargs):
response_obj = {
- 'name': get_name(self),
- 'description': get_description(self),
+ 'name': self.get_name(),
+ 'description': self.get_description(),
'renders': self._rendered_media_types,
'parses': self._parsed_media_types,
}
@@ -184,21 +240,18 @@ class ModelView(View):
"""
resource = resources.ModelResource
-
class InstanceModelView(InstanceMixin, ReadModelMixin, UpdateModelMixin, DeleteModelMixin, ModelView):
"""
A view which provides default operations for read/update/delete against a model instance.
"""
_suffix = 'Instance'
-
class ListModelView(ListModelMixin, ModelView):
"""
A view which provides default operations for list, against a model in the database.
"""
_suffix = 'List'
-
class ListOrCreateModelView(ListModelMixin, CreateModelMixin, ModelView):
"""
A view which provides default operations for list and create, against a model in the database.
--
cgit v1.2.3
From 049d417ebc2c88b6aeed7946efdb3baeab9a4432 Mon Sep 17 00:00:00 2001
From: Ben Timby
Date: Tue, 24 Jan 2012 14:11:10 -0500
Subject: Reverted formatting
---
djangorestframework/renderers.py | 18 +++++++++---------
djangorestframework/templates/renderer.html | 11 ++++++++---
djangorestframework/views.py | 10 ++++++----
3 files changed, 23 insertions(+), 16 deletions(-)
diff --git a/djangorestframework/renderers.py b/djangorestframework/renderers.py
index 5338c383..bb0f789a 100644
--- a/djangorestframework/renderers.py
+++ b/djangorestframework/renderers.py
@@ -225,7 +225,6 @@ class DocumentingTemplateRenderer(BaseRenderer):
return content
-
def _get_form_instance(self, view, method):
"""
Get a form, possibly bound to either the input or output data.
@@ -261,7 +260,6 @@ class DocumentingTemplateRenderer(BaseRenderer):
return form_instance
-
def _get_generic_content_form(self, view):
"""
Returns a form that allows for arbitrary content types to be tunneled via standard HTML forms
@@ -340,7 +338,7 @@ class DocumentingTemplateRenderer(BaseRenderer):
context = RequestContext(self.view.request, {
'content': content,
'view': self.view,
- 'request': self.view.request, # TODO: remove
+ 'request': self.view.request, # TODO: remove
'response': self.view.response,
'description': description,
'name': name,
@@ -402,12 +400,14 @@ class DocumentingPlainTextRenderer(DocumentingTemplateRenderer):
template = 'renderer.txt'
-DEFAULT_RENDERERS = ( JSONRenderer,
- JSONPRenderer,
- DocumentingHTMLRenderer,
- DocumentingXHTMLRenderer,
- DocumentingPlainTextRenderer,
- XMLRenderer )
+DEFAULT_RENDERERS = (
+ JSONRenderer,
+ JSONPRenderer,
+ DocumentingHTMLRenderer,
+ DocumentingXHTMLRenderer,
+ DocumentingPlainTextRenderer,
+ XMLRenderer
+)
if YAMLRenderer:
DEFAULT_RENDERERS += (YAMLRenderer,)
diff --git a/djangorestframework/templates/renderer.html b/djangorestframework/templates/renderer.html
index 3b33a6ff..ff761023 100644
--- a/djangorestframework/templates/renderer.html
+++ b/djangorestframework/templates/renderer.html
@@ -11,9 +11,14 @@
/* Custom styles */
.version{font-size:8px;}
-
-
- Django REST framework - {{ name }}
+ {% if ADMIN_MEDIA_PREFIX %}
+
+
+ {% else %}
+
+
+ {% endif %}
+ Django REST framework - {{ name }}
diff --git a/djangorestframework/views.py b/djangorestframework/views.py
index 9dd84bf2..d89c2afd 100644
--- a/djangorestframework/views.py
+++ b/djangorestframework/views.py
@@ -27,7 +27,6 @@ __all__ = (
)
-
class View(ResourceMixin, RequestMixin, ResponseMixin, AuthMixin, DjangoView):
"""
Handles incoming requests and maps them to REST operations.
@@ -50,13 +49,13 @@ class View(ResourceMixin, RequestMixin, ResponseMixin, AuthMixin, DjangoView):
List of parsers the resource can parse the request with.
"""
- authentication = ( authentication.UserLoggedInAuthentication,
- authentication.BasicAuthentication )
+ authentication = (authentication.UserLoggedInAuthentication,
+ authentication.BasicAuthentication)
"""
List of all authenticating methods to attempt.
"""
- permissions = ( permissions.FullAnonAccess, )
+ permissions = (permissions.FullAnonAccess,)
"""
List of all permissions that must be checked.
"""
@@ -240,18 +239,21 @@ class ModelView(View):
"""
resource = resources.ModelResource
+
class InstanceModelView(InstanceMixin, ReadModelMixin, UpdateModelMixin, DeleteModelMixin, ModelView):
"""
A view which provides default operations for read/update/delete against a model instance.
"""
_suffix = 'Instance'
+
class ListModelView(ListModelMixin, ModelView):
"""
A view which provides default operations for list, against a model in the database.
"""
_suffix = 'List'
+
class ListOrCreateModelView(ListModelMixin, CreateModelMixin, ModelView):
"""
A view which provides default operations for list and create, against a model in the database.
--
cgit v1.2.3
From d3ec860dd1fedf27042b3d08bf6c7e4f4adbddc9 Mon Sep 17 00:00:00 2001
From: Ben Timby
Date: Tue, 24 Jan 2012 14:36:34 -0500
Subject: Modified test case to pass regardless of JSON library
---
djangorestframework/tests/renderers.py | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/djangorestframework/tests/renderers.py b/djangorestframework/tests/renderers.py
index adb46f7f..77b5430f 100644
--- a/djangorestframework/tests/renderers.py
+++ b/djangorestframework/tests/renderers.py
@@ -1,3 +1,5 @@
+import re
+
from django.conf.urls.defaults import patterns, url
from django.test import TestCase
@@ -187,6 +189,8 @@ class JSONRendererTests(TestCase):
obj = {'foo': ['bar', 'baz']}
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):
--
cgit v1.2.3