Fixed #32260 -- Made View.as_view() do not use update_wrapper().
View.as_view() should not use update_wrapper() for copying attributes it's unintended and have side-effects such as adding `self` to the signature. This also fixes system check for arguments of custom error handler views with class-based views. Co-authored-by: Nick Pope <nick.pope@flightdataservices.com>
This commit is contained in:
parent
0c0b87725b
commit
7c08f26bf0
|
@ -1,5 +1,4 @@
|
|||
import logging
|
||||
from functools import update_wrapper
|
||||
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
from django.http import (
|
||||
|
@ -71,12 +70,16 @@ class View:
|
|||
view.view_class = cls
|
||||
view.view_initkwargs = initkwargs
|
||||
|
||||
# take name and docstring from class
|
||||
update_wrapper(view, cls, updated=())
|
||||
# __name__ and __qualname__ are intentionally left unchanged as
|
||||
# view_class should be used to robustly determine the name of the view
|
||||
# instead.
|
||||
view.__doc__ = cls.__doc__
|
||||
view.__module__ = cls.__module__
|
||||
view.__annotations__ = cls.dispatch.__annotations__
|
||||
# Copy possible attributes set by decorators, e.g. @csrf_exempt, from
|
||||
# the dispatch method.
|
||||
view.__dict__.update(cls.dispatch.__dict__)
|
||||
|
||||
# and possible attributes set by decorators
|
||||
# like csrf_exempt from dispatch
|
||||
update_wrapper(view, cls.dispatch, assigned=())
|
||||
return view
|
||||
|
||||
def setup(self, request, *args, **kwargs):
|
||||
|
|
|
@ -167,20 +167,41 @@ class UpdatedToPathTests(SimpleTestCase):
|
|||
|
||||
class CheckCustomErrorHandlersTests(SimpleTestCase):
|
||||
|
||||
@override_settings(ROOT_URLCONF='check_framework.urls.bad_error_handlers')
|
||||
def test_bad_handlers(self):
|
||||
@override_settings(
|
||||
ROOT_URLCONF='check_framework.urls.bad_function_based_error_handlers',
|
||||
)
|
||||
def test_bad_function_based_handlers(self):
|
||||
result = check_url_config(None)
|
||||
self.assertEqual(len(result), 4)
|
||||
for code, num_params, error in zip([400, 403, 404, 500], [2, 2, 2, 1], result):
|
||||
with self.subTest('handler{}'.format(code)):
|
||||
self.assertEqual(error, Error(
|
||||
"The custom handler{} view "
|
||||
"'check_framework.urls.bad_error_handlers.bad_handler' "
|
||||
"The custom handler{} view 'check_framework.urls."
|
||||
"bad_function_based_error_handlers.bad_handler' "
|
||||
"does not take the correct number of arguments (request{})."
|
||||
.format(code, ', exception' if num_params == 2 else ''),
|
||||
id='urls.E007',
|
||||
))
|
||||
|
||||
@override_settings(
|
||||
ROOT_URLCONF='check_framework.urls.bad_class_based_error_handlers',
|
||||
)
|
||||
def test_bad_class_based_handlers(self):
|
||||
result = check_url_config(None)
|
||||
self.assertEqual(len(result), 4)
|
||||
for code, num_params, error in zip([400, 403, 404, 500], [2, 2, 2, 1], result):
|
||||
with self.subTest('handler%s' % code):
|
||||
self.assertEqual(error, Error(
|
||||
"The custom handler%s view 'check_framework.urls."
|
||||
"bad_class_based_error_handlers.HandlerView.as_view."
|
||||
"<locals>.view' does not take the correct number of "
|
||||
"arguments (request%s)." % (
|
||||
code,
|
||||
', exception' if num_params == 2 else '',
|
||||
),
|
||||
id='urls.E007',
|
||||
))
|
||||
|
||||
@override_settings(ROOT_URLCONF='check_framework.urls.bad_error_handlers_invalid_path')
|
||||
def test_bad_handlers_invalid_path(self):
|
||||
result = check_url_config(None)
|
||||
|
@ -204,8 +225,17 @@ class CheckCustomErrorHandlersTests(SimpleTestCase):
|
|||
id='urls.E008',
|
||||
))
|
||||
|
||||
@override_settings(ROOT_URLCONF='check_framework.urls.good_error_handlers')
|
||||
def test_good_handlers(self):
|
||||
@override_settings(
|
||||
ROOT_URLCONF='check_framework.urls.good_function_based_error_handlers',
|
||||
)
|
||||
def test_good_function_based_handlers(self):
|
||||
result = check_url_config(None)
|
||||
self.assertEqual(result, [])
|
||||
|
||||
@override_settings(
|
||||
ROOT_URLCONF='check_framework.urls.good_class_based_error_handlers',
|
||||
)
|
||||
def test_good_class_based_handlers(self):
|
||||
result = check_url_config(None)
|
||||
self.assertEqual(result, [])
|
||||
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
urlpatterns = []
|
||||
|
||||
|
||||
class HandlerView:
|
||||
@classmethod
|
||||
def as_view(cls):
|
||||
def view():
|
||||
pass
|
||||
|
||||
return view
|
||||
|
||||
|
||||
handler400 = HandlerView.as_view()
|
||||
handler403 = HandlerView.as_view()
|
||||
handler404 = HandlerView.as_view()
|
||||
handler500 = HandlerView.as_view()
|
|
@ -0,0 +1,9 @@
|
|||
from django.views.generic import View
|
||||
|
||||
urlpatterns = []
|
||||
|
||||
|
||||
handler400 = View.as_view()
|
||||
handler403 = View.as_view()
|
||||
handler404 = View.as_view()
|
||||
handler500 = View.as_view()
|
|
@ -172,12 +172,16 @@ class ViewTest(SimpleTestCase):
|
|||
|
||||
def test_class_attributes(self):
|
||||
"""
|
||||
The callable returned from as_view() has proper
|
||||
docstring, name and module.
|
||||
The callable returned from as_view() has proper special attributes.
|
||||
"""
|
||||
self.assertEqual(SimpleView.__doc__, SimpleView.as_view().__doc__)
|
||||
self.assertEqual(SimpleView.__name__, SimpleView.as_view().__name__)
|
||||
self.assertEqual(SimpleView.__module__, SimpleView.as_view().__module__)
|
||||
cls = SimpleView
|
||||
view = cls.as_view()
|
||||
self.assertEqual(view.__doc__, cls.__doc__)
|
||||
self.assertEqual(view.__name__, 'view')
|
||||
self.assertEqual(view.__module__, cls.__module__)
|
||||
self.assertEqual(view.__qualname__, f'{cls.as_view.__qualname__}.<locals>.view')
|
||||
self.assertEqual(view.__annotations__, cls.dispatch.__annotations__)
|
||||
self.assertFalse(hasattr(view, '__wrapped__'))
|
||||
|
||||
def test_dispatch_decoration(self):
|
||||
"""
|
||||
|
|
Loading…
Reference in New Issue