diff --git a/docs/api-guide/settings.md b/docs/api-guide/settings.md index 13f96f9a3..e399b441a 100644 --- a/docs/api-guide/settings.md +++ b/docs/api-guide/settings.md @@ -313,10 +313,11 @@ A string representing the function that should be used when generating view name This should be a function with the following signature: - view_name(cls, suffix=None) + view_name(cls, suffix=None, request=None) * `cls`: The view class. Typically the name function would inspect the name of the class when generating a descriptive name, by accessing `cls.__name__`. * `suffix`: The optional suffix used when differentiating individual views in a viewset. +* `request`: The request object (if exists). The name function can inspect the request to decide how to generate the name. Default: `'rest_framework.views.get_view_name'` @@ -328,10 +329,11 @@ This setting can be changed to support markup styles other than the default mark This should be a function with the following signature: - view_description(cls, html=False) + view_description(cls, html=False, request=None) * `cls`: The view class. Typically the description function would inspect the docstring of the class when generating a description, by accessing `cls.__doc__` * `html`: A boolean indicating if HTML output is required. `True` when used in the browsable API, and `False` when used in generating `OPTIONS` responses. +* `request`: The request object (if exists). The description function can inspect the request to decide how to generate the description. Default: `'rest_framework.views.get_view_description'` diff --git a/rest_framework/renderers.py b/rest_framework/renderers.py index a27160d46..6d2cef8d0 100644 --- a/rest_framework/renderers.py +++ b/rest_framework/renderers.py @@ -10,6 +10,8 @@ from __future__ import unicode_literals import copy import json +import inspect +import warnings from django import forms from django.core.exceptions import ImproperlyConfigured from django.http.multipartparser import parse_header @@ -562,11 +564,25 @@ class BrowsableAPIRenderer(BaseRenderer): return GenericContentForm() - def get_name(self, view): - return view.get_view_name() + def get_name(self, view, request): + if len(inspect.getargspec(view.get_view_name).args) == 1: + warnings.warn( + 'The `APIView.get_view_name` without `request` argument is deprecated.' + 'Add a `request` argument to your `APIView.get_view_name`.', + PendingDeprecationWarning + ) + return view.get_view_name() + return view.get_view_name(request) - def get_description(self, view): - return view.get_view_description(html=True) + def get_description(self, view, request): + if len(inspect.getargspec(view.get_view_description).args) == 2: + warnings.warn( + 'The `APIView.get_view_description` without `request` argument is deprecated.' + 'Add a `request` argument to your `APIView.get_view_description`.', + PendingDeprecationWarning + ) + return view.get_view_description(html=True) + return view.get_view_description(html=True, request=request) def get_breadcrumbs(self, request): return get_breadcrumbs(request.path) @@ -585,13 +601,33 @@ class BrowsableAPIRenderer(BaseRenderer): raw_data_patch_form = self.get_raw_data_form(view, 'PATCH', request) raw_data_put_or_patch_form = raw_data_put_form or raw_data_patch_form + if len(inspect.getargspec(self.get_description).args) == 2: + warnings.warn( + 'The `BrowsableAPIRenderer.get_description` without `request` argument is deprecated.' + 'Add a `request` argument to your `BrowsableAPIRenderer.get_description`.', + PendingDeprecationWarning + ) + description = self.get_description(view) + else: + description = self.get_description(view, request) + + if len(inspect.getargspec(self.get_name).args) == 2: + warnings.warn( + 'The `BrowsableAPIRenderer.get_name` without `request` argument is deprecated.' + 'Add a `request` argument to your `BrowsableAPIRenderer.get_name`.', + PendingDeprecationWarning + ) + name = self.get_name(view) + else: + name = self.get_name(view, request) + context = { 'content': self.get_content(renderer, data, accepted_media_type, renderer_context), 'view': view, 'request': request, 'response': response, - 'description': self.get_description(view), - 'name': self.get_name(view), + 'description': description, + 'name': name, 'version': VERSION, 'breadcrumblist': self.get_breadcrumbs(request), 'allowed_methods': view.allowed_methods, diff --git a/rest_framework/views.py b/rest_framework/views.py index 853e64614..7135abfd7 100644 --- a/rest_framework/views.py +++ b/rest_framework/views.py @@ -3,6 +3,8 @@ Provides an APIView class that is the base of all views in REST framework. """ from __future__ import unicode_literals +import inspect +import warnings from django.core.exceptions import PermissionDenied from django.http import Http404 from django.utils.datastructures import SortedDict @@ -15,7 +17,7 @@ from rest_framework.settings import api_settings from rest_framework.utils import formatting -def get_view_name(view_cls, suffix=None): +def get_view_name(view_cls, suffix=None, request=None): """ Given a view class, return a textual name to represent the view. This name is used in the browsable API, and in OPTIONS responses. @@ -31,7 +33,7 @@ def get_view_name(view_cls, suffix=None): return name -def get_view_description(view_cls, html=False): +def get_view_description(view_cls, html=False, request=None): """ Given a view class, return a textual description to represent the view. This name is used in the browsable API, and in OPTIONS responses. @@ -176,21 +178,35 @@ class APIView(View): 'request': getattr(self, 'request', None) } - def get_view_name(self): + def get_view_name(self, request=None): """ Return the view name, as used in OPTIONS responses and in the browsable API. """ func = self.settings.VIEW_NAME_FUNCTION - return func(self.__class__, getattr(self, 'suffix', None)) + if len(inspect.getargspec(func).args) == 2: + warnings.warn( + 'The `VIEW_NAME_FUNCTION` without `request` argument is deprecated.' + 'Add a `request` argument to your `VIEW_NAME_FUNCTION`.', + PendingDeprecationWarning + ) + return func(self.__class__, getattr(self, 'suffix', None)) + return func(self.__class__, getattr(self, 'suffix', None), request) - def get_view_description(self, html=False): + def get_view_description(self, html=False, request=None): """ Return some descriptive text for the view, as used in OPTIONS responses and in the browsable API. """ func = self.settings.VIEW_DESCRIPTION_FUNCTION - return func(self.__class__, html) + if len(inspect.getargspec(func).args) == 2: + warnings.warn( + 'The `VIEW_DESCRIPTION_FUNCTION` without `request` argument is deprecated.' + 'Add a `request` argument to your `VIEW_DESCRIPTION_FUNCTION`.', + PendingDeprecationWarning + ) + return func(self.__class__, html) + return func(self.__class__, html, request) # API policy instantiation methods @@ -418,8 +434,8 @@ class APIView(View): # generic views override this implementation and add additional # information for POST and PUT methods, based on the serializer. ret = SortedDict() - ret['name'] = self.get_view_name() - ret['description'] = self.get_view_description() + ret['name'] = self.get_view_name(request) + ret['description'] = self.get_view_description(False, request) ret['renders'] = [renderer.media_type for renderer in self.renderer_classes] ret['parses'] = [parser.media_type for parser in self.parser_classes] return ret