mirror of
https://github.com/encode/django-rest-framework.git
synced 2025-03-03 19:00:17 +03:00
Merge branch 'master' of git://github.com/chrispaolini/django-rest-framework into chrispaolini-master
This commit is contained in:
commit
a8aabe23c9
|
@ -24,7 +24,6 @@ from rest_framework.settings import api_settings
|
||||||
from rest_framework.request import clone_request
|
from rest_framework.request import clone_request
|
||||||
from rest_framework.utils import encoders
|
from rest_framework.utils import encoders
|
||||||
from rest_framework.utils.breadcrumbs import get_breadcrumbs
|
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
|
from rest_framework import exceptions, parsers, status, VERSION
|
||||||
|
|
||||||
|
|
||||||
|
@ -498,10 +497,10 @@ class BrowsableAPIRenderer(BaseRenderer):
|
||||||
return GenericContentForm()
|
return GenericContentForm()
|
||||||
|
|
||||||
def get_name(self, view):
|
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):
|
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):
|
def get_breadcrumbs(self, request):
|
||||||
return get_breadcrumbs(request.path)
|
return get_breadcrumbs(request.path)
|
||||||
|
|
|
@ -69,6 +69,10 @@ DEFAULTS = {
|
||||||
'PAGINATE_BY': None,
|
'PAGINATE_BY': None,
|
||||||
'PAGINATE_BY_PARAM': 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
|
# Authentication
|
||||||
'UNAUTHENTICATED_USER': 'django.contrib.auth.models.AnonymousUser',
|
'UNAUTHENTICATED_USER': 'django.contrib.auth.models.AnonymousUser',
|
||||||
'UNAUTHENTICATED_TOKEN': None,
|
'UNAUTHENTICATED_TOKEN': None,
|
||||||
|
@ -125,6 +129,8 @@ IMPORT_STRINGS = (
|
||||||
'TEST_REQUEST_RENDERER_CLASSES',
|
'TEST_REQUEST_RENDERER_CLASSES',
|
||||||
'UNAUTHENTICATED_USER',
|
'UNAUTHENTICATED_USER',
|
||||||
'UNAUTHENTICATED_TOKEN',
|
'UNAUTHENTICATED_TOKEN',
|
||||||
|
'VIEW_NAME_FUNCTION',
|
||||||
|
'VIEW_DESCRIPTION_FUNCTION'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,6 @@ from rest_framework.compat import apply_markdown, smart_text
|
||||||
from rest_framework.views import APIView
|
from rest_framework.views import APIView
|
||||||
from rest_framework.tests.description import ViewWithNonASCIICharactersInDocstring
|
from rest_framework.tests.description import ViewWithNonASCIICharactersInDocstring
|
||||||
from rest_framework.tests.description import UTF8_TEST_DOCSTRING
|
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.
|
# We check that docstrings get nicely un-indented.
|
||||||
DESCRIPTION = """an example docstring
|
DESCRIPTION = """an example docstring
|
||||||
|
@ -58,7 +57,7 @@ class TestViewNamesAndDescriptions(TestCase):
|
||||||
"""
|
"""
|
||||||
class MockView(APIView):
|
class MockView(APIView):
|
||||||
pass
|
pass
|
||||||
self.assertEqual(get_view_name(MockView), 'Mock')
|
self.assertEqual(MockView().get_view_name(), 'Mock')
|
||||||
|
|
||||||
def test_view_description_uses_docstring(self):
|
def test_view_description_uses_docstring(self):
|
||||||
"""Ensure view descriptions are based on the docstring."""
|
"""Ensure view descriptions are based on the docstring."""
|
||||||
|
@ -78,7 +77,7 @@ class TestViewNamesAndDescriptions(TestCase):
|
||||||
|
|
||||||
# hash style header #"""
|
# hash style header #"""
|
||||||
|
|
||||||
self.assertEqual(get_view_description(MockView), DESCRIPTION)
|
self.assertEqual(MockView().get_view_description(), DESCRIPTION)
|
||||||
|
|
||||||
def test_view_description_supports_unicode(self):
|
def test_view_description_supports_unicode(self):
|
||||||
"""
|
"""
|
||||||
|
@ -86,7 +85,7 @@ class TestViewNamesAndDescriptions(TestCase):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
get_view_description(ViewWithNonASCIICharactersInDocstring),
|
ViewWithNonASCIICharactersInDocstring().get_view_description(),
|
||||||
smart_text(UTF8_TEST_DOCSTRING)
|
smart_text(UTF8_TEST_DOCSTRING)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -97,7 +96,7 @@ class TestViewNamesAndDescriptions(TestCase):
|
||||||
"""
|
"""
|
||||||
class MockView(APIView):
|
class MockView(APIView):
|
||||||
pass
|
pass
|
||||||
self.assertEqual(get_view_description(MockView), '')
|
self.assertEqual(MockView().get_view_description(), '')
|
||||||
|
|
||||||
def test_markdown(self):
|
def test_markdown(self):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
from django.core.urlresolvers import resolve, get_script_prefix
|
from django.core.urlresolvers import resolve, get_script_prefix
|
||||||
from rest_framework.utils.formatting import get_view_name
|
|
||||||
|
|
||||||
|
|
||||||
def get_breadcrumbs(url):
|
def get_breadcrumbs(url):
|
||||||
|
@ -29,8 +28,8 @@ def get_breadcrumbs(url):
|
||||||
# Don't list the same view twice in a row.
|
# Don't list the same view twice in a row.
|
||||||
# Probably an optional trailing slash.
|
# Probably an optional trailing slash.
|
||||||
if not seen or seen[-1] != view:
|
if not seen or seen[-1] != view:
|
||||||
suffix = getattr(view, 'suffix', None)
|
instance = view.cls()
|
||||||
name = get_view_name(view.cls, suffix)
|
name = instance.get_view_name()
|
||||||
breadcrumbs_list.insert(0, (name, prefix + url))
|
breadcrumbs_list.insert(0, (name, prefix + url))
|
||||||
seen.append(view)
|
seen.append(view)
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@ from django.utils.html import escape
|
||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
from rest_framework.compat import apply_markdown, smart_text
|
from rest_framework.compat import apply_markdown, smart_text
|
||||||
import re
|
import re
|
||||||
|
from rest_framework.settings import api_settings
|
||||||
|
|
||||||
|
|
||||||
def _remove_trailing_string(content, trailing):
|
def _remove_trailing_string(content, trailing):
|
||||||
|
@ -44,31 +45,6 @@ def _camelcase_to_spaces(content):
|
||||||
content = re.sub(camelcase_boundry, ' \\1', content).strip()
|
content = re.sub(camelcase_boundry, ' \\1', content).strip()
|
||||||
return ' '.join(content.split('_')).title()
|
return ' '.join(content.split('_')).title()
|
||||||
|
|
||||||
|
|
||||||
def get_view_name(cls, suffix=None):
|
|
||||||
"""
|
|
||||||
Return a formatted name for an `APIView` class or `@api_view` function.
|
|
||||||
"""
|
|
||||||
name = cls.__name__
|
|
||||||
name = _remove_trailing_string(name, 'View')
|
|
||||||
name = _remove_trailing_string(name, 'ViewSet')
|
|
||||||
name = _camelcase_to_spaces(name)
|
|
||||||
if suffix:
|
|
||||||
name += ' ' + suffix
|
|
||||||
return name
|
|
||||||
|
|
||||||
|
|
||||||
def get_view_description(cls, html=False):
|
|
||||||
"""
|
|
||||||
Return a description for an `APIView` class or `@api_view` function.
|
|
||||||
"""
|
|
||||||
description = cls.__doc__ or ''
|
|
||||||
description = _remove_leading_indent(smart_text(description))
|
|
||||||
if html:
|
|
||||||
return markup_description(description)
|
|
||||||
return description
|
|
||||||
|
|
||||||
|
|
||||||
def markup_description(description):
|
def markup_description(description):
|
||||||
"""
|
"""
|
||||||
Apply HTML markup to the given description.
|
Apply HTML markup to the given description.
|
||||||
|
@ -78,3 +54,21 @@ def markup_description(description):
|
||||||
else:
|
else:
|
||||||
description = escape(description).replace('\n', '<br />')
|
description = escape(description).replace('\n', '<br />')
|
||||||
return mark_safe(description)
|
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
|
||||||
|
|
||||||
|
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
|
|
@ -12,7 +12,6 @@ from rest_framework.compat import View, HttpResponseBase
|
||||||
from rest_framework.request import Request
|
from rest_framework.request import Request
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from rest_framework.settings import api_settings
|
from rest_framework.settings import api_settings
|
||||||
from rest_framework.utils.formatting import get_view_name, get_view_description
|
|
||||||
|
|
||||||
|
|
||||||
class APIView(View):
|
class APIView(View):
|
||||||
|
@ -25,6 +24,9 @@ class APIView(View):
|
||||||
permission_classes = api_settings.DEFAULT_PERMISSION_CLASSES
|
permission_classes = api_settings.DEFAULT_PERMISSION_CLASSES
|
||||||
content_negotiation_class = api_settings.DEFAULT_CONTENT_NEGOTIATION_CLASS
|
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
|
@classmethod
|
||||||
def as_view(cls, **initkwargs):
|
def as_view(cls, **initkwargs):
|
||||||
"""
|
"""
|
||||||
|
@ -157,6 +159,21 @@ class APIView(View):
|
||||||
self._negotiator = self.content_negotiation_class()
|
self._negotiator = self.content_negotiation_class()
|
||||||
return self._negotiator
|
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
|
# API policy implementation methods
|
||||||
|
|
||||||
def perform_content_negotiation(self, request, force=False):
|
def perform_content_negotiation(self, request, force=False):
|
||||||
|
@ -342,16 +359,12 @@ class APIView(View):
|
||||||
Return a dictionary of metadata about the view.
|
Return a dictionary of metadata about the view.
|
||||||
Used to return responses for OPTIONS requests.
|
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
|
# By default we can't provide any form-like information, however the
|
||||||
# generic views override this implementation and add additional
|
# generic views override this implementation and add additional
|
||||||
# information for POST and PUT methods, based on the serializer.
|
# information for POST and PUT methods, based on the serializer.
|
||||||
ret = SortedDict()
|
ret = SortedDict()
|
||||||
ret['name'] = get_view_name(self.__class__, view_name_suffix)
|
ret['name'] = self.get_view_name()
|
||||||
ret['description'] = get_view_description(self.__class__)
|
ret['description'] = self.get_view_description()
|
||||||
ret['renders'] = [renderer.media_type for renderer in self.renderer_classes]
|
ret['renders'] = [renderer.media_type for renderer in self.renderer_classes]
|
||||||
ret['parses'] = [parser.media_type for parser in self.parser_classes]
|
ret['parses'] = [parser.media_type for parser in self.parser_classes]
|
||||||
return ret
|
return ret
|
||||||
|
|
Loading…
Reference in New Issue
Block a user