This commit is contained in:
Oguntunde Caleb 2023-05-12 02:07:14 +01:00
parent db2c45c729
commit 05b389f668
3 changed files with 162 additions and 167 deletions

View File

@ -41,6 +41,7 @@ The `api_settings` object will check for any user-defined settings, and otherwis
*The following settings control the basic API policies, and are applied to every `APIView` class-based view, or `@api_view` function based view.* *The following settings control the basic API policies, and are applied to every `APIView` class-based view, or `@api_view` function based view.*
<<<<<<< HEAD
#### DEFAULT_MIDDLEWARE_CLASSES #### DEFAULT_MIDDLEWARE_CLASSES
A list or tuple of middleware classes, that is run prior to calling the method handler. A list or tuple of middleware classes, that is run prior to calling the method handler.
@ -48,6 +49,8 @@ A list or tuple of middleware classes, that is run prior to calling the method h
Default: `[]` Default: `[]`
=======
>>>>>>> parent of 700cbf19 (Add middleware classes support that run after drf mutate request)
#### DEFAULT_RENDERER_CLASSES #### DEFAULT_RENDERER_CLASSES
A list or tuple of renderer classes, that determines the default set of renderers that may be used when returning a `Response` object. A list or tuple of renderer classes, that determines the default set of renderers that may be used when returning a `Response` object.

View File

@ -19,7 +19,6 @@ REST framework settings, checking for user settings first, then falling
back to the defaults. back to the defaults.
""" """
from django.conf import settings from django.conf import settings
# Import from `django.core.signals` instead of the official location # Import from `django.core.signals` instead of the official location
# `django.test.signals` to avoid importing the test module unnecessarily. # `django.test.signals` to avoid importing the test module unnecessarily.
from django.core.signals import setting_changed from django.core.signals import setting_changed
@ -28,118 +27,138 @@ from django.utils.module_loading import import_string
from rest_framework import ISO_8601 from rest_framework import ISO_8601
DEFAULTS = { DEFAULTS = {
<<<<<<< HEAD
# custom middleware class to run prior to calling the method handler # custom middleware class to run prior to calling the method handler
"DEFAULT_MIDDLEWARE_CLASSES": [], "DEFAULT_MIDDLEWARE_CLASSES": [],
=======
>>>>>>> parent of 700cbf19 (Add middleware classes support that run after drf mutate request)
# Base API policies # Base API policies
"DEFAULT_RENDERER_CLASSES": [ 'DEFAULT_RENDERER_CLASSES': [
"rest_framework.renderers.JSONRenderer", 'rest_framework.renderers.JSONRenderer',
"rest_framework.renderers.BrowsableAPIRenderer", 'rest_framework.renderers.BrowsableAPIRenderer',
], ],
"DEFAULT_PARSER_CLASSES": [ 'DEFAULT_PARSER_CLASSES': [
"rest_framework.parsers.JSONParser", 'rest_framework.parsers.JSONParser',
"rest_framework.parsers.FormParser", 'rest_framework.parsers.FormParser',
"rest_framework.parsers.MultiPartParser", 'rest_framework.parsers.MultiPartParser'
], ],
"DEFAULT_AUTHENTICATION_CLASSES": [ 'DEFAULT_AUTHENTICATION_CLASSES': [
"rest_framework.authentication.SessionAuthentication", 'rest_framework.authentication.SessionAuthentication',
"rest_framework.authentication.BasicAuthentication", 'rest_framework.authentication.BasicAuthentication'
], ],
"DEFAULT_PERMISSION_CLASSES": [ 'DEFAULT_PERMISSION_CLASSES': [
"rest_framework.permissions.AllowAny", 'rest_framework.permissions.AllowAny',
], ],
"DEFAULT_THROTTLE_CLASSES": [], 'DEFAULT_THROTTLE_CLASSES': [],
"DEFAULT_CONTENT_NEGOTIATION_CLASS": "rest_framework.negotiation.DefaultContentNegotiation", 'DEFAULT_CONTENT_NEGOTIATION_CLASS': 'rest_framework.negotiation.DefaultContentNegotiation',
"DEFAULT_METADATA_CLASS": "rest_framework.metadata.SimpleMetadata", 'DEFAULT_METADATA_CLASS': 'rest_framework.metadata.SimpleMetadata',
"DEFAULT_VERSIONING_CLASS": None, 'DEFAULT_VERSIONING_CLASS': None,
# Generic view behavior # Generic view behavior
"DEFAULT_PAGINATION_CLASS": None, 'DEFAULT_PAGINATION_CLASS': None,
"DEFAULT_FILTER_BACKENDS": [], 'DEFAULT_FILTER_BACKENDS': [],
# Schema # Schema
"DEFAULT_SCHEMA_CLASS": "rest_framework.schemas.openapi.AutoSchema", 'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.openapi.AutoSchema',
# Throttling # Throttling
"DEFAULT_THROTTLE_RATES": { 'DEFAULT_THROTTLE_RATES': {
"user": None, 'user': None,
"anon": None, 'anon': None,
}, },
"NUM_PROXIES": None, 'NUM_PROXIES': None,
# Pagination # Pagination
"PAGE_SIZE": None, 'PAGE_SIZE': None,
# Filtering # Filtering
"SEARCH_PARAM": "search", 'SEARCH_PARAM': 'search',
"ORDERING_PARAM": "ordering", 'ORDERING_PARAM': 'ordering',
# Versioning # Versioning
"DEFAULT_VERSION": None, 'DEFAULT_VERSION': None,
"ALLOWED_VERSIONS": None, 'ALLOWED_VERSIONS': None,
"VERSION_PARAM": "version", 'VERSION_PARAM': 'version',
# Authentication # Authentication
"UNAUTHENTICATED_USER": "django.contrib.auth.models.AnonymousUser", 'UNAUTHENTICATED_USER': 'django.contrib.auth.models.AnonymousUser',
"UNAUTHENTICATED_TOKEN": None, 'UNAUTHENTICATED_TOKEN': None,
# View configuration # View configuration
"VIEW_NAME_FUNCTION": "rest_framework.views.get_view_name", 'VIEW_NAME_FUNCTION': 'rest_framework.views.get_view_name',
"VIEW_DESCRIPTION_FUNCTION": "rest_framework.views.get_view_description", 'VIEW_DESCRIPTION_FUNCTION': 'rest_framework.views.get_view_description',
# Exception handling # Exception handling
"EXCEPTION_HANDLER": "rest_framework.views.exception_handler", 'EXCEPTION_HANDLER': 'rest_framework.views.exception_handler',
"NON_FIELD_ERRORS_KEY": "non_field_errors", 'NON_FIELD_ERRORS_KEY': 'non_field_errors',
# Testing # Testing
"TEST_REQUEST_RENDERER_CLASSES": [ 'TEST_REQUEST_RENDERER_CLASSES': [
"rest_framework.renderers.MultiPartRenderer", 'rest_framework.renderers.MultiPartRenderer',
"rest_framework.renderers.JSONRenderer", 'rest_framework.renderers.JSONRenderer'
], ],
"TEST_REQUEST_DEFAULT_FORMAT": "multipart", 'TEST_REQUEST_DEFAULT_FORMAT': 'multipart',
# Hyperlink settings # Hyperlink settings
"URL_FORMAT_OVERRIDE": "format", 'URL_FORMAT_OVERRIDE': 'format',
"FORMAT_SUFFIX_KWARG": "format", 'FORMAT_SUFFIX_KWARG': 'format',
"URL_FIELD_NAME": "url", 'URL_FIELD_NAME': 'url',
# Input and output formats # Input and output formats
"DATE_FORMAT": ISO_8601, 'DATE_FORMAT': ISO_8601,
"DATE_INPUT_FORMATS": [ISO_8601], 'DATE_INPUT_FORMATS': [ISO_8601],
"DATETIME_FORMAT": ISO_8601,
"DATETIME_INPUT_FORMATS": [ISO_8601], 'DATETIME_FORMAT': ISO_8601,
"TIME_FORMAT": ISO_8601, 'DATETIME_INPUT_FORMATS': [ISO_8601],
"TIME_INPUT_FORMATS": [ISO_8601],
'TIME_FORMAT': ISO_8601,
'TIME_INPUT_FORMATS': [ISO_8601],
# Encoding # Encoding
"UNICODE_JSON": True, 'UNICODE_JSON': True,
"COMPACT_JSON": True, 'COMPACT_JSON': True,
"STRICT_JSON": True, 'STRICT_JSON': True,
"COERCE_DECIMAL_TO_STRING": True, 'COERCE_DECIMAL_TO_STRING': True,
"UPLOADED_FILES_USE_URL": True, 'UPLOADED_FILES_USE_URL': True,
# Browseable API # Browseable API
"HTML_SELECT_CUTOFF": 1000, 'HTML_SELECT_CUTOFF': 1000,
"HTML_SELECT_CUTOFF_TEXT": "More than {count} items...", 'HTML_SELECT_CUTOFF_TEXT': "More than {count} items...",
# Schemas # Schemas
"SCHEMA_COERCE_PATH_PK": True, 'SCHEMA_COERCE_PATH_PK': True,
"SCHEMA_COERCE_METHOD_NAMES": {"retrieve": "read", "destroy": "delete"}, 'SCHEMA_COERCE_METHOD_NAMES': {
'retrieve': 'read',
'destroy': 'delete'
},
} }
# List of settings that may be in string import notation. # List of settings that may be in string import notation.
IMPORT_STRINGS = [ IMPORT_STRINGS = [
"MIDDLEWARE_CLASSES", 'DEFAULT_RENDERER_CLASSES',
"DEFAULT_RENDERER_CLASSES", 'DEFAULT_PARSER_CLASSES',
"DEFAULT_PARSER_CLASSES", 'DEFAULT_AUTHENTICATION_CLASSES',
"DEFAULT_AUTHENTICATION_CLASSES", 'DEFAULT_PERMISSION_CLASSES',
"DEFAULT_PERMISSION_CLASSES", 'DEFAULT_THROTTLE_CLASSES',
"DEFAULT_THROTTLE_CLASSES", 'DEFAULT_CONTENT_NEGOTIATION_CLASS',
"DEFAULT_CONTENT_NEGOTIATION_CLASS", 'DEFAULT_METADATA_CLASS',
"DEFAULT_METADATA_CLASS", 'DEFAULT_VERSIONING_CLASS',
"DEFAULT_VERSIONING_CLASS", 'DEFAULT_PAGINATION_CLASS',
"DEFAULT_PAGINATION_CLASS", 'DEFAULT_FILTER_BACKENDS',
"DEFAULT_FILTER_BACKENDS", 'DEFAULT_SCHEMA_CLASS',
"DEFAULT_SCHEMA_CLASS", 'EXCEPTION_HANDLER',
"EXCEPTION_HANDLER", 'TEST_REQUEST_RENDERER_CLASSES',
"TEST_REQUEST_RENDERER_CLASSES", 'UNAUTHENTICATED_USER',
"UNAUTHENTICATED_USER", 'UNAUTHENTICATED_TOKEN',
"UNAUTHENTICATED_TOKEN", 'VIEW_NAME_FUNCTION',
"VIEW_NAME_FUNCTION", 'VIEW_DESCRIPTION_FUNCTION'
"VIEW_DESCRIPTION_FUNCTION",
] ]
# List of settings that have been removed # List of settings that have been removed
REMOVED_SETTINGS = [ REMOVED_SETTINGS = [
"PAGINATE_BY", 'PAGINATE_BY', 'PAGINATE_BY_PARAM', 'MAX_PAGINATE_BY',
"PAGINATE_BY_PARAM",
"MAX_PAGINATE_BY",
] ]
@ -164,12 +183,7 @@ def import_from_string(val, setting_name):
try: try:
return import_string(val) return import_string(val)
except ImportError as e: except ImportError as e:
msg = "Could not import '%s' for API setting '%s'. %s: %s." % ( msg = "Could not import '%s' for API setting '%s'. %s: %s." % (val, setting_name, e.__class__.__name__, e)
val,
setting_name,
e.__class__.__name__,
e,
)
raise ImportError(msg) raise ImportError(msg)
@ -189,7 +203,6 @@ class APISettings:
under the REST_FRAMEWORK name. It is not intended to be used by 3rd-party under the REST_FRAMEWORK name. It is not intended to be used by 3rd-party
apps, and test helpers like `override_settings` may not work as expected. apps, and test helpers like `override_settings` may not work as expected.
""" """
def __init__(self, user_settings=None, defaults=None, import_strings=None): def __init__(self, user_settings=None, defaults=None, import_strings=None):
if user_settings: if user_settings:
self._user_settings = self.__check_user_settings(user_settings) self._user_settings = self.__check_user_settings(user_settings)
@ -199,8 +212,8 @@ class APISettings:
@property @property
def user_settings(self): def user_settings(self):
if not hasattr(self, "_user_settings"): if not hasattr(self, '_user_settings'):
self._user_settings = getattr(settings, "REST_FRAMEWORK", {}) self._user_settings = getattr(settings, 'REST_FRAMEWORK', {})
return self._user_settings return self._user_settings
def __getattr__(self, attr): def __getattr__(self, attr):
@ -227,26 +240,23 @@ class APISettings:
SETTINGS_DOC = "https://www.django-rest-framework.org/api-guide/settings/" SETTINGS_DOC = "https://www.django-rest-framework.org/api-guide/settings/"
for setting in REMOVED_SETTINGS: for setting in REMOVED_SETTINGS:
if setting in user_settings: if setting in user_settings:
raise RuntimeError( raise RuntimeError("The '%s' setting has been removed. Please refer to '%s' for available settings." % (setting, SETTINGS_DOC))
"The '%s' setting has been removed. Please refer to '%s' for available settings."
% (setting, SETTINGS_DOC)
)
return user_settings return user_settings
def reload(self): def reload(self):
for attr in self._cached_attrs: for attr in self._cached_attrs:
delattr(self, attr) delattr(self, attr)
self._cached_attrs.clear() self._cached_attrs.clear()
if hasattr(self, "_user_settings"): if hasattr(self, '_user_settings'):
delattr(self, "_user_settings") delattr(self, '_user_settings')
api_settings = APISettings(None, DEFAULTS, IMPORT_STRINGS) api_settings = APISettings(None, DEFAULTS, IMPORT_STRINGS)
def reload_api_settings(*args, **kwargs): def reload_api_settings(*args, **kwargs):
setting = kwargs["setting"] setting = kwargs['setting']
if setting == "REST_FRAMEWORK": if setting == 'REST_FRAMEWORK':
api_settings.reload() api_settings.reload()

View File

@ -27,19 +27,19 @@ def get_view_name(view):
This function is the default for the `VIEW_NAME_FUNCTION` setting. This function is the default for the `VIEW_NAME_FUNCTION` setting.
""" """
# Name may be set by some Views, such as a ViewSet. # Name may be set by some Views, such as a ViewSet.
name = getattr(view, "name", None) name = getattr(view, 'name', None)
if name is not None: if name is not None:
return name return name
name = view.__class__.__name__ name = view.__class__.__name__
name = formatting.remove_trailing_string(name, "View") name = formatting.remove_trailing_string(name, 'View')
name = formatting.remove_trailing_string(name, "ViewSet") name = formatting.remove_trailing_string(name, 'ViewSet')
name = formatting.camelcase_to_spaces(name) name = formatting.camelcase_to_spaces(name)
# Suffix may be set by some Views, such as a ViewSet. # Suffix may be set by some Views, such as a ViewSet.
suffix = getattr(view, "suffix", None) suffix = getattr(view, 'suffix', None)
if suffix: if suffix:
name += " " + suffix name += ' ' + suffix
return name return name
@ -52,9 +52,9 @@ def get_view_description(view, html=False):
This function is the default for the `VIEW_DESCRIPTION_FUNCTION` setting. This function is the default for the `VIEW_DESCRIPTION_FUNCTION` setting.
""" """
# Description may be set by some Views, such as a ViewSet. # Description may be set by some Views, such as a ViewSet.
description = getattr(view, "description", None) description = getattr(view, 'description', None)
if description is None: if description is None:
description = view.__class__.__doc__ or "" description = view.__class__.__doc__ or ''
description = formatting.dedent(smart_str(description)) description = formatting.dedent(smart_str(description))
if html: if html:
@ -64,7 +64,7 @@ def get_view_description(view, html=False):
def set_rollback(): def set_rollback():
for db in connections.all(): for db in connections.all():
if db.settings_dict["ATOMIC_REQUESTS"] and db.in_atomic_block: if db.settings_dict['ATOMIC_REQUESTS'] and db.in_atomic_block:
db.set_rollback(True) db.set_rollback(True)
@ -85,15 +85,15 @@ def exception_handler(exc, context):
if isinstance(exc, exceptions.APIException): if isinstance(exc, exceptions.APIException):
headers = {} headers = {}
if getattr(exc, "auth_header", None): if getattr(exc, 'auth_header', None):
headers["WWW-Authenticate"] = exc.auth_header headers['WWW-Authenticate'] = exc.auth_header
if getattr(exc, "wait", None): if getattr(exc, 'wait', None):
headers["Retry-After"] = "%d" % exc.wait headers['Retry-After'] = '%d' % exc.wait
if isinstance(exc.detail, (list, dict)): if isinstance(exc.detail, (list, dict)):
data = exc.detail data = exc.detail
else: else:
data = {"detail": exc.detail} data = {'detail': exc.detail}
set_rollback() set_rollback()
return Response(data, status=exc.status_code, headers=headers) return Response(data, status=exc.status_code, headers=headers)
@ -102,8 +102,12 @@ def exception_handler(exc, context):
class APIView(View): class APIView(View):
# The following policies may be set at either globally, or per-view. # The following policies may be set at either globally, or per-view.
<<<<<<< HEAD
middleware_classes = api_settings.DEFAULT_MIDDLEWARE_CLASSES middleware_classes = api_settings.DEFAULT_MIDDLEWARE_CLASSES
=======
>>>>>>> parent of 700cbf19 (Add middleware classes support that run after drf mutate request)
renderer_classes = api_settings.DEFAULT_RENDERER_CLASSES renderer_classes = api_settings.DEFAULT_RENDERER_CLASSES
parser_classes = api_settings.DEFAULT_PARSER_CLASSES parser_classes = api_settings.DEFAULT_PARSER_CLASSES
authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES
@ -126,15 +130,13 @@ class APIView(View):
This allows us to discover information about the view when we do URL This allows us to discover information about the view when we do URL
reverse lookups. Used for breadcrumb generation. reverse lookups. Used for breadcrumb generation.
""" """
if isinstance(getattr(cls, "queryset", None), models.query.QuerySet): if isinstance(getattr(cls, 'queryset', None), models.query.QuerySet):
def force_evaluation(): def force_evaluation():
raise RuntimeError( raise RuntimeError(
"Do not evaluate the `.queryset` attribute directly, " 'Do not evaluate the `.queryset` attribute directly, '
"as the result will be cached and reused between requests. " 'as the result will be cached and reused between requests. '
"Use `.all()` or call `.get_queryset()` instead." 'Use `.all()` or call `.get_queryset()` instead.'
) )
cls.queryset._fetch_all = force_evaluation cls.queryset._fetch_all = force_evaluation
view = super().as_view(**initkwargs) view = super().as_view(**initkwargs)
@ -155,10 +157,10 @@ class APIView(View):
@property @property
def default_response_headers(self): def default_response_headers(self):
headers = { headers = {
"Allow": ", ".join(self.allowed_methods), 'Allow': ', '.join(self.allowed_methods),
} }
if len(self.renderer_classes) > 1: if len(self.renderer_classes) > 1:
headers["Vary"] = "Accept" headers['Vary'] = 'Accept'
return headers return headers
def http_method_not_allowed(self, request, *args, **kwargs): def http_method_not_allowed(self, request, *args, **kwargs):
@ -199,9 +201,9 @@ class APIView(View):
# Note: Additionally `request` and `encoding` will also be added # Note: Additionally `request` and `encoding` will also be added
# to the context by the Request object. # to the context by the Request object.
return { return {
"view": self, 'view': self,
"args": getattr(self, "args", ()), 'args': getattr(self, 'args', ()),
"kwargs": getattr(self, "kwargs", {}), 'kwargs': getattr(self, 'kwargs', {})
} }
def get_renderer_context(self): def get_renderer_context(self):
@ -212,10 +214,10 @@ class APIView(View):
# Note: Additionally 'response' will also be added to the context, # Note: Additionally 'response' will also be added to the context,
# by the Response object. # by the Response object.
return { return {
"view": self, 'view': self,
"args": getattr(self, "args", ()), 'args': getattr(self, 'args', ()),
"kwargs": getattr(self, "kwargs", {}), 'kwargs': getattr(self, 'kwargs', {}),
"request": getattr(self, "request", None), 'request': getattr(self, 'request', None)
} }
def get_exception_handler_context(self): def get_exception_handler_context(self):
@ -224,10 +226,10 @@ class APIView(View):
as the `context` argument. as the `context` argument.
""" """
return { return {
"view": self, 'view': self,
"args": getattr(self, "args", ()), 'args': getattr(self, 'args', ()),
"kwargs": getattr(self, "kwargs", {}), 'kwargs': getattr(self, 'kwargs', {}),
"request": getattr(self, "request", None), 'request': getattr(self, 'request', None)
} }
def get_view_name(self): def get_view_name(self):
@ -289,7 +291,7 @@ class APIView(View):
""" """
Instantiate and return the content negotiation class to use. Instantiate and return the content negotiation class to use.
""" """
if not getattr(self, "_negotiator", None): if not getattr(self, '_negotiator', None):
self._negotiator = self.content_negotiation_class() self._negotiator = self.content_negotiation_class()
return self._negotiator return self._negotiator
@ -334,8 +336,8 @@ class APIView(View):
if not permission.has_permission(request, self): if not permission.has_permission(request, self):
self.permission_denied( self.permission_denied(
request, request,
message=getattr(permission, "message", None), message=getattr(permission, 'message', None),
code=getattr(permission, "code", None), code=getattr(permission, 'code', None)
) )
def check_object_permissions(self, request, obj): def check_object_permissions(self, request, obj):
@ -347,8 +349,8 @@ class APIView(View):
if not permission.has_object_permission(request, self, obj): if not permission.has_object_permission(request, self, obj):
self.permission_denied( self.permission_denied(
request, request,
message=getattr(permission, "message", None), message=getattr(permission, 'message', None),
code=getattr(permission, "code", None), code=getattr(permission, 'code', None)
) )
def check_throttles(self, request): def check_throttles(self, request):
@ -365,7 +367,8 @@ class APIView(View):
# Filter out `None` values which may happen in case of config / rate # Filter out `None` values which may happen in case of config / rate
# changes, see #1438 # changes, see #1438
durations = [ durations = [
duration for duration in throttle_durations if duration is not None duration for duration in throttle_durations
if duration is not None
] ]
duration = max(durations, default=None) duration = max(durations, default=None)
@ -383,23 +386,6 @@ class APIView(View):
# Dispatch methods # Dispatch methods
def get_middleware_classes(self):
"""
get list of middleware class instance
"""
return [middleware() for middleware in self.middleware_classes]
def initialize_middleware_classes(self, request):
"""
Run custom middleware classes before prior to calling the method handler
"""
for middleware in self.get_middleware_classes():
middleware(request)
return request # Return mutated request
def initialize_request(self, request, *args, **kwargs): def initialize_request(self, request, *args, **kwargs):
""" """
Returns the initial request object. Returns the initial request object.
@ -411,7 +397,7 @@ class APIView(View):
parsers=self.get_parsers(), parsers=self.get_parsers(),
authenticators=self.get_authenticators(), authenticators=self.get_authenticators(),
negotiator=self.get_content_negotiator(), negotiator=self.get_content_negotiator(),
parser_context=parser_context, parser_context=parser_context
) )
def initial(self, request, *args, **kwargs): def initial(self, request, *args, **kwargs):
@ -433,21 +419,19 @@ class APIView(View):
self.check_permissions(request) self.check_permissions(request)
self.check_throttles(request) self.check_throttles(request)
# authentication and other task ran before final mutation
self.initialize_middleware_classes(request)
def finalize_response(self, request, response, *args, **kwargs): def finalize_response(self, request, response, *args, **kwargs):
""" """
Returns the final response object. Returns the final response object.
""" """
# Make the error obvious if a proper response is not returned # Make the error obvious if a proper response is not returned
assert isinstance(response, HttpResponseBase), ( assert isinstance(response, HttpResponseBase), (
"Expected a `Response`, `HttpResponse` or `HttpStreamingResponse` " 'Expected a `Response`, `HttpResponse` or `HttpStreamingResponse` '
"to be returned from the view, but received a `%s`" % type(response) 'to be returned from the view, but received a `%s`'
% type(response)
) )
if isinstance(response, Response): if isinstance(response, Response):
if not getattr(request, "accepted_renderer", None): if not getattr(request, 'accepted_renderer', None):
neg = self.perform_content_negotiation(request, force=True) neg = self.perform_content_negotiation(request, force=True)
request.accepted_renderer, request.accepted_media_type = neg request.accepted_renderer, request.accepted_media_type = neg
@ -456,7 +440,7 @@ class APIView(View):
response.renderer_context = self.get_renderer_context() response.renderer_context = self.get_renderer_context()
# Add new vary headers to the response instead of overwriting. # Add new vary headers to the response instead of overwriting.
vary_headers = self.headers.pop("Vary", None) vary_headers = self.headers.pop('Vary', None)
if vary_headers is not None: if vary_headers is not None:
patch_vary_headers(response, cc_delim_re.split(vary_headers)) patch_vary_headers(response, cc_delim_re.split(vary_headers))
@ -470,9 +454,8 @@ class APIView(View):
Handle any exception that occurs, by returning an appropriate response, Handle any exception that occurs, by returning an appropriate response,
or re-raising the error. or re-raising the error.
""" """
if isinstance( if isinstance(exc, (exceptions.NotAuthenticated,
exc, (exceptions.NotAuthenticated, exceptions.AuthenticationFailed) exceptions.AuthenticationFailed)):
):
# WWW-Authenticate header for 401 responses, else coerce to 403 # WWW-Authenticate header for 401 responses, else coerce to 403
auth_header = self.get_authenticate_header(self.request) auth_header = self.get_authenticate_header(self.request)
@ -495,8 +478,8 @@ class APIView(View):
def raise_uncaught_exception(self, exc): def raise_uncaught_exception(self, exc):
if settings.DEBUG: if settings.DEBUG:
request = self.request request = self.request
renderer_format = getattr(request.accepted_renderer, "format") renderer_format = getattr(request.accepted_renderer, 'format')
use_plaintext_traceback = renderer_format not in ("html", "api", "admin") use_plaintext_traceback = renderer_format not in ('html', 'api', 'admin')
request.force_plaintext_errors(use_plaintext_traceback) request.force_plaintext_errors(use_plaintext_traceback)
raise exc raise exc
@ -519,9 +502,8 @@ class APIView(View):
# Get the appropriate handler method # Get the appropriate handler method
if request.method.lower() in self.http_method_names: if request.method.lower() in self.http_method_names:
handler = getattr( handler = getattr(self, request.method.lower(),
self, request.method.lower(), self.http_method_not_allowed self.http_method_not_allowed)
)
else: else:
handler = self.http_method_not_allowed handler = self.http_method_not_allowed