mirror of
				https://github.com/django/django.git
				synced 2025-10-31 09:41:08 +00:00 
			
		
		
		
	WIP: Refs #35281 -- Unified and generalized request error handling.
This commit is contained in:
		| @@ -1,9 +1,3 @@ | ||||
| from django.urls import include | ||||
| from django.views import defaults | ||||
|  | ||||
| __all__ = ["handler400", "handler403", "handler404", "handler500", "include"] | ||||
|  | ||||
| handler400 = defaults.bad_request | ||||
| handler403 = defaults.permission_denied | ||||
| handler404 = defaults.page_not_found | ||||
| handler500 = defaults.server_error | ||||
| __all__ = ["include"] | ||||
|   | ||||
| @@ -131,7 +131,7 @@ def check_custom_error_handlers(app_configs, **kwargs): | ||||
|     errors = [] | ||||
|     # All handlers take (request, exception) arguments except handler500 | ||||
|     # which takes (request). | ||||
|     for status_code, num_parameters in [(400, 2), (403, 2), (404, 2), (500, 1)]: | ||||
|     for status_code, num_parameters in [(400, 2), (403, 2), (404, 2), (500, 2)]: | ||||
|         try: | ||||
|             handler = resolver.resolve_error_handler(status_code) | ||||
|         except (ImportError, ViewDoesNotExist) as e: | ||||
|   | ||||
| @@ -211,6 +211,23 @@ class HttpResponseBase: | ||||
|     def get(self, header, alternate=None): | ||||
|         return self.headers.get(header, alternate) | ||||
|  | ||||
|     @classmethod | ||||
|     def response_class_by_status_code(cls, status_code): | ||||
|         return { | ||||
|             200: HttpResponse, | ||||
|             301: HttpResponsePermanentRedirect, | ||||
|             302: HttpResponseRedirect, | ||||
|             304: HttpResponseNotModified, | ||||
|             307: HttpResponseRedirect, | ||||
|             308: HttpResponsePermanentRedirect, | ||||
|             400: HttpResponseBadRequest, | ||||
|             403: HttpResponseForbidden, | ||||
|             404: HttpResponseNotFound, | ||||
|             405: HttpResponseNotAllowed, | ||||
|             410: HttpResponseGone, | ||||
|             500: HttpResponseServerError, | ||||
|         }.get(status_code, cls) | ||||
|  | ||||
|     def set_cookie( | ||||
|         self, | ||||
|         key, | ||||
|   | ||||
| @@ -10,6 +10,7 @@ import functools | ||||
| import inspect | ||||
| import re | ||||
| import string | ||||
| import warnings | ||||
| from importlib import import_module | ||||
| from pickle import PicklingError | ||||
| from urllib.parse import quote | ||||
| @@ -21,6 +22,7 @@ from django.core.checks import Error, Warning | ||||
| from django.core.checks.urls import check_resolver | ||||
| from django.core.exceptions import ImproperlyConfigured | ||||
| from django.utils.datastructures import MultiValueDict | ||||
| from django.utils.deprecation import RemovedInDjango61Warning | ||||
| from django.utils.functional import cached_property | ||||
| from django.utils.http import RFC3986_SUBDELIMS, escape_leading_slashes | ||||
| from django.utils.regex_helper import _lazy_re_compile, normalize | ||||
| @@ -728,15 +730,26 @@ class URLResolver: | ||||
|             raise ImproperlyConfigured(msg.format(name=self.urlconf_name)) from e | ||||
|         return patterns | ||||
|  | ||||
|     def resolve_error_handler(self, view_type): | ||||
|         callback = getattr(self.urlconf_module, "handler%s" % view_type, None) | ||||
|         if not callback: | ||||
|     def resolve_error_handler(self, status_code): | ||||
|         # RemovedInDjango61Warning. | ||||
|         callback = getattr(self.urlconf_module, f"handler{status_code}", None) | ||||
|         if callback: | ||||
|             warnings.warn( | ||||
|                 "handler<status> custom error handlers are deprecated, please " | ||||
|                 "replace them by a generic `error_handler` view function.", | ||||
|                 RemovedInDjango61Warning, | ||||
|             ) | ||||
|             return get_callable(callback) | ||||
|         error_view = getattr(self.urlconf_module, "error_handler", None) | ||||
|         if error_view: | ||||
|             error_view.status_code = status_code | ||||
|         else: | ||||
|             # No handler specified in file; use lazy import, since | ||||
|             # django.conf.urls imports this file. | ||||
|             from django.conf import urls | ||||
|             # django.views.defaults imports this file. | ||||
|             from django.views.defaults import DefaultErrorView | ||||
|  | ||||
|             callback = getattr(urls, "handler%s" % view_type) | ||||
|         return get_callable(callback) | ||||
|             error_view = DefaultErrorView.as_view(status_code=status_code) | ||||
|         return error_view | ||||
|  | ||||
|     def reverse(self, lookup_view, *args, **kwargs): | ||||
|         return self._reverse_with_prefix(lookup_view, "", *args, **kwargs) | ||||
|   | ||||
| @@ -1,13 +1,17 @@ | ||||
| from urllib.parse import quote | ||||
|  | ||||
| from django.http import ( | ||||
|     HttpResponse, | ||||
|     HttpResponseBadRequest, | ||||
|     HttpResponseForbidden, | ||||
|     HttpResponseNotFound, | ||||
|     HttpResponseServerError, | ||||
| ) | ||||
| from django.template import Context, Engine, TemplateDoesNotExist, loader | ||||
| from django.utils.decorators import method_decorator | ||||
| from django.views.debug import DEBUG_ENGINE | ||||
| from django.views.decorators.csrf import requires_csrf_token | ||||
| from django.views.generic.base import ContextMixin, View | ||||
|  | ||||
| ERROR_404_TEMPLATE_NAME = "404.html" | ||||
| ERROR_403_TEMPLATE_NAME = "403.html" | ||||
| @@ -148,3 +152,63 @@ def permission_denied(request, exception, template_name=ERROR_403_TEMPLATE_NAME) | ||||
|     return HttpResponseForbidden( | ||||
|         template.render(request=request, context={"exception": str(exception)}) | ||||
|     ) | ||||
|  | ||||
|  | ||||
| @method_decorator(requires_csrf_token, name="dispatch") | ||||
| class DefaultErrorView(ContextMixin, View): | ||||
|     status_code = None | ||||
|     context_by_status = { | ||||
|         400: {"title": "Bad Request (400)", "details": ""}, | ||||
|         403: {"title": "403 Forbidden", "details": ""}, | ||||
|         404: { | ||||
|             "title": "Not Found", | ||||
|             "details": "The requested resource was not found on this server.", | ||||
|         }, | ||||
|         500: {"title": "Server Error (500)", "details": ""}, | ||||
|     } | ||||
|  | ||||
|     def setup(self, request, exception=None, **kwargs): | ||||
|         self.exception = exception | ||||
|         return super().setup(request, **kwargs) | ||||
|  | ||||
|     def get(self, request, *args, **kwargs): | ||||
|         response_class = HttpResponse.response_class_by_status_code(self.status_code) | ||||
|         context = self.get_context_data(**kwargs) | ||||
|         try: | ||||
|             template = loader.get_template(self.get_template_name()) | ||||
|             content = template.render(context, request) | ||||
|         except TemplateDoesNotExist: | ||||
|             template = DEBUG_ENGINE.from_string(ERROR_PAGE_TEMPLATE % context) | ||||
|             content = template.render(context=Context(context)) | ||||
|         return response_class(content, status=self.status_code) | ||||
|  | ||||
|     def post(self, *args, **kwargs): | ||||
|         return self.get(*args, **kwargs) | ||||
|  | ||||
|     def get_template_name(self): | ||||
|         return f"{self.status_code}.html" | ||||
|  | ||||
|     def get_context_data(self, **kwargs): | ||||
|         context = super().get_context_data(**kwargs) | ||||
|         context |= self.context_by_status.get( | ||||
|             self.status_code, {"title": f"Error ({self.status_code})", "details": ""} | ||||
|         ) | ||||
|         context |= { | ||||
|             "request_path": quote(self.request.path), | ||||
|             "exception": self.exception_as_string(), | ||||
|         } | ||||
|         return context | ||||
|  | ||||
|     def exception_as_string(self): | ||||
|         if self.status_code == 404: | ||||
|             # Try to get an "interesting" exception message, if any (and not the | ||||
|             # ugly Resolver404 dictionary) | ||||
|             try: | ||||
|                 message = self.exception.args[0] | ||||
|             except (AttributeError, IndexError): | ||||
|                 pass | ||||
|             else: | ||||
|                 if isinstance(message, str): | ||||
|                     return message | ||||
|             return self.exception.__class__.__name__ | ||||
|         return str(self.exception) | ||||
|   | ||||
| @@ -1179,9 +1179,8 @@ details on these changes. | ||||
|   ``django.contrib.gis.utils`` will be removed. | ||||
|  | ||||
| * ``django.conf.urls.defaults`` will be removed. The functions | ||||
|   ``include()``, ``patterns()``, and ``url()``, plus | ||||
|   :data:`~django.conf.urls.handler404` and :data:`~django.conf.urls.handler500` | ||||
|   are now available through ``django.conf.urls``. | ||||
|   ``include()``, ``patterns()``, and ``url()``, plus ``handler404` and | ||||
|   ``handler500`` are now available through ``django.conf.urls``. | ||||
|  | ||||
| * The functions ``setup_environ()`` and ``execute_manager()`` will be removed | ||||
|   from :mod:`django.core.management`. This also means that the old (pre-1.4) | ||||
|   | ||||
| @@ -187,6 +187,11 @@ By default, this is :func:`django.views.defaults.bad_request`. If you | ||||
| implement a custom view, be sure it accepts ``request`` and ``exception`` | ||||
| arguments and returns an :class:`~django.http.HttpResponseBadRequest`. | ||||
|  | ||||
| .. deprecated:: 5.2 | ||||
|  | ||||
|     The ``handler<code>`` handlers are deprecated and should be replaced by a | ||||
|     generic ``error_handler`` view function. | ||||
|  | ||||
| ``handler403`` | ||||
| ============== | ||||
|  | ||||
| @@ -200,6 +205,12 @@ By default, this is :func:`django.views.defaults.permission_denied`. If you | ||||
| implement a custom view, be sure it accepts ``request`` and ``exception`` | ||||
| arguments and returns an :class:`~django.http.HttpResponseForbidden`. | ||||
|  | ||||
|  | ||||
| .. deprecated:: 5.2 | ||||
|  | ||||
|     The ``handler<code>`` handlers are deprecated and should be replaced by a | ||||
|     generic ``error_handler`` view function. | ||||
|  | ||||
| ``handler404`` | ||||
| ============== | ||||
|  | ||||
| @@ -212,6 +223,12 @@ By default, this is :func:`django.views.defaults.page_not_found`. If you | ||||
| implement a custom view, be sure it accepts ``request`` and ``exception`` | ||||
| arguments and returns an :class:`~django.http.HttpResponseNotFound`. | ||||
|  | ||||
|  | ||||
| .. deprecated:: 5.2 | ||||
|  | ||||
|     The ``handler<code>`` handlers are deprecated and should be replaced by a | ||||
|     generic ``error_handler`` view function. | ||||
|  | ||||
| ``handler500`` | ||||
| ============== | ||||
|  | ||||
| @@ -224,3 +241,8 @@ have runtime errors in view code. | ||||
| By default, this is :func:`django.views.defaults.server_error`. If you | ||||
| implement a custom view, be sure it accepts a ``request`` argument and returns | ||||
| an :class:`~django.http.HttpResponseServerError`. | ||||
|  | ||||
| .. deprecated:: 5.2 | ||||
|  | ||||
|     The ``handler<code>`` handlers are deprecated and should be replaced by a | ||||
|     generic ``error_handler`` view function. | ||||
|   | ||||
| @@ -58,8 +58,61 @@ parameter will be transparently passed to the view. | ||||
| Error views | ||||
| =========== | ||||
|  | ||||
| Django comes with a few views by default for handling HTTP errors. To override | ||||
| these with your own custom views, see :ref:`customizing-error-views`. | ||||
| When any uncaught exception is produced by a view of your site, Django is | ||||
| calling an error handling view, which is by default the following view. | ||||
|  | ||||
| .. _default_error_view: | ||||
|  | ||||
| The default error view | ||||
| ---------------------- | ||||
|  | ||||
| .. class:: defaults.DefaultErrorView | ||||
|  | ||||
|     .. versionadded:: 5.2  | ||||
|  | ||||
|     The view is a class-based-view inheriting from | ||||
|     :class:`~django.views.generic.base.ContextMixin` | ||||
|     and class:`~django.views.generic.base.View`, so refer to theses classes for | ||||
|     common attributes and methods. | ||||
|  | ||||
|     .. attribute:: status_code | ||||
|  | ||||
|         The status code of the HTTP response to return. | ||||
|  | ||||
|     .. attribute:: exception | ||||
|  | ||||
|         The exception that produced the error leading to the call of this error | ||||
|         view. | ||||
|  | ||||
|     .. method:: get_template_name() | ||||
|  | ||||
|         Return by default a name on the pattern ``<status_code>.html``. | ||||
|  | ||||
|     .. method:: exception_as_string() | ||||
|  | ||||
|         Produce a string from the exception having triggered the error view. | ||||
|  | ||||
| If you provide any template in your root template directory named after an error | ||||
| response code (``404.html``, ``500.html``, etc.), that template will be used to | ||||
| produce an error path. Otherwise, Django will produce a very simple page | ||||
| containing an error title and a standard message. | ||||
|  | ||||
| The context used to render the error templates contains the following keys: | ||||
|  | ||||
|     * ``title``: A short title for the error (like ``Not Found`` for a 404 | ||||
|       error). | ||||
|     * ``details``: A short message explaining the error, possibley empty. | ||||
|     * ``request_path``: The original request path of the view producing the | ||||
|       error, quoted to prevent a content injection attack. | ||||
|     * ``exception``: The uncaught exception that triggered the error view, as a | ||||
|       string. | ||||
|  | ||||
| which either produces a "Not | ||||
| Found" message or loads and renders the template ``404.html`` if you created it | ||||
| in your root template directory. | ||||
|  | ||||
| To override this default view with your custom view, see | ||||
| :ref:`customizing-error-views`. | ||||
|  | ||||
| .. _http_not_found_view: | ||||
|  | ||||
| @@ -68,6 +121,11 @@ The 404 (page not found) view | ||||
|  | ||||
| .. function:: defaults.page_not_found(request, exception, template_name='404.html') | ||||
|  | ||||
| .. deprecated:: 5.2 | ||||
|  | ||||
|     This view is deprecated as it has been replaced by the :ref:`default error | ||||
|     view <_default_error_view>`. | ||||
|  | ||||
| When you raise :exc:`~django.http.Http404` from within a view, Django loads a | ||||
| special view devoted to handling 404 errors. By default, it's the view | ||||
| :func:`django.views.defaults.page_not_found`, which either produces a "Not | ||||
| @@ -99,6 +157,11 @@ The 500 (server error) view | ||||
|  | ||||
| .. function:: defaults.server_error(request, template_name='500.html') | ||||
|  | ||||
| .. deprecated:: 5.2 | ||||
|  | ||||
|     This view is deprecated as it has been replaced by the :ref:`default error | ||||
|     view <_default_error_view>`. | ||||
|  | ||||
| Similarly, Django executes special-case behavior in the case of runtime errors | ||||
| in view code. If a view results in an exception, Django will, by default, call | ||||
| the view ``django.views.defaults.server_error``, which either produces a | ||||
| @@ -119,6 +182,11 @@ The 403 (HTTP Forbidden) view | ||||
|  | ||||
| .. function:: defaults.permission_denied(request, exception, template_name='403.html') | ||||
|  | ||||
| .. deprecated:: 5.2 | ||||
|  | ||||
|     This view is deprecated as it has been replaced by the :ref:`default error | ||||
|     view <_default_error_view>`. | ||||
|  | ||||
| In the same vein as the 404 and 500 views, Django has a view to handle 403 | ||||
| Forbidden errors. If a view results in a 403 exception then Django will, by | ||||
| default, call the view ``django.views.defaults.permission_denied``. | ||||
| @@ -148,6 +216,11 @@ The 400 (bad request) view | ||||
|  | ||||
| .. function:: defaults.bad_request(request, exception, template_name='400.html') | ||||
|  | ||||
| .. deprecated:: 5.2 | ||||
|  | ||||
|     This view is deprecated as it has been replaced by the :ref:`default error | ||||
|     view <_default_error_view>`. | ||||
|  | ||||
| When a :exc:`~django.core.exceptions.SuspiciousOperation` is raised in Django, | ||||
| it may be handled by a component of Django (for example resetting the session | ||||
| data). If not specifically handled, Django will consider the current request a | ||||
|   | ||||
| @@ -342,25 +342,28 @@ Error handling | ||||
| When Django can't find a match for the requested URL, or when an exception is | ||||
| raised, Django invokes an error-handling view. | ||||
|  | ||||
| The views to use for these cases are specified by four variables. Their | ||||
| default values should suffice for most projects, but further customization is | ||||
| possible by overriding their default values. | ||||
| The default view Django uses for these cases is | ||||
| :class:`django.views.defaults.DefaultErrorView`. This view should suffice for | ||||
| most projects but further customization is possible by defining a custom view | ||||
| to handle errors, either by subclassing this view or by creating your own. | ||||
|  | ||||
| See the documentation on :ref:`customizing error views | ||||
| See the documentation on :ref:`customizing error view | ||||
| <customizing-error-views>` for the full details. | ||||
|  | ||||
| Such values can be set in your root URLconf. Setting these variables in any | ||||
| other URLconf will have no effect. | ||||
| .. versionchanged:: 5.2 | ||||
|  | ||||
| Values must be callables, or strings representing the full Python import path | ||||
| to the view that should be called to handle the error condition at hand. | ||||
|     In previous versions, the views to use for these cases are specified by four | ||||
|     variables. | ||||
|  | ||||
| The variables are: | ||||
|     Values must be callables, or strings representing the full Python import path | ||||
|     to the view that should be called to handle the error condition at hand. | ||||
|  | ||||
| * ``handler400`` -- See :data:`django.conf.urls.handler400`. | ||||
| * ``handler403`` -- See :data:`django.conf.urls.handler403`. | ||||
| * ``handler404`` -- See :data:`django.conf.urls.handler404`. | ||||
| * ``handler500`` -- See :data:`django.conf.urls.handler500`. | ||||
|     The variables are: | ||||
|  | ||||
|     * ``handler400`` | ||||
|     * ``handler403`` | ||||
|     * ``handler404`` | ||||
|     * ``handler500`` | ||||
|  | ||||
| .. _including-other-urlconfs: | ||||
|  | ||||
|   | ||||
| @@ -138,33 +138,46 @@ template. | ||||
|  | ||||
| .. _customizing-error-views: | ||||
|  | ||||
| Customizing error views | ||||
| ======================= | ||||
| Customizing error view | ||||
| ====================== | ||||
|  | ||||
| The default error views in Django should suffice for most web applications, | ||||
| but can easily be overridden if you need any custom behavior. Specify the | ||||
| handlers as seen below in your URLconf (setting them anywhere else will have no | ||||
| effect). | ||||
| The default error view in Django should suffice for most web applications, but | ||||
| but can easily be overridden if you need any custom behavior. | ||||
|  | ||||
| The :func:`~django.views.defaults.page_not_found` view is overridden by | ||||
| :data:`~django.conf.urls.handler404`:: | ||||
| The default error view can be overridden by setting a view in the | ||||
| ``error_handler`` variable in your URLconf (setting it anywhere else will have | ||||
| no effect):: | ||||
|  | ||||
|     handler404 = "mysite.views.my_custom_page_not_found_view" | ||||
|     error_handler = views.my_custom_error_view | ||||
|  | ||||
| The :func:`~django.views.defaults.server_error` view is overridden by | ||||
| :data:`~django.conf.urls.handler500`:: | ||||
| or if you have a class-based-view:: | ||||
|  | ||||
|     handler500 = "mysite.views.my_custom_error_view" | ||||
|     error_handler = views.MyCustomErrorView.as_view() | ||||
|  | ||||
| The :func:`~django.views.defaults.permission_denied` view is overridden by | ||||
| :data:`~django.conf.urls.handler403`:: | ||||
| .. versionchanged:: 5.2 | ||||
|  | ||||
|     handler403 = "mysite.views.my_custom_permission_denied_view" | ||||
|     On previous versions, there are multiple variables to define to override | ||||
|     particular views. | ||||
|  | ||||
| The :func:`~django.views.defaults.bad_request` view is overridden by | ||||
| :data:`~django.conf.urls.handler400`:: | ||||
|     The :func:`~django.views.defaults.page_not_found` view is overridden by | ||||
|     :data:`~django.conf.urls.handler404`:: | ||||
|  | ||||
|     handler400 = "mysite.views.my_custom_bad_request_view" | ||||
|         handler404 = "mysite.views.my_custom_page_not_found_view" | ||||
|  | ||||
|     The :func:`~django.views.defaults.server_error` view is overridden by | ||||
|     :data:`~django.conf.urls.handler500`:: | ||||
|  | ||||
|         handler500 = "mysite.views.my_custom_error_view" | ||||
|  | ||||
|     The :func:`~django.views.defaults.permission_denied` view is overridden by | ||||
|     :data:`~django.conf.urls.handler403`:: | ||||
|  | ||||
|         handler403 = "mysite.views.my_custom_permission_denied_view" | ||||
|  | ||||
|     The :func:`~django.views.defaults.bad_request` view is overridden by | ||||
|     :data:`~django.conf.urls.handler400`:: | ||||
|  | ||||
|         handler400 = "mysite.views.my_custom_bad_request_view" | ||||
|  | ||||
| .. seealso:: | ||||
|  | ||||
|   | ||||
| @@ -8,8 +8,9 @@ from django.core.checks.urls import ( | ||||
|     check_url_settings, | ||||
|     get_warning_for_invalid_pattern, | ||||
| ) | ||||
| from django.test import SimpleTestCase | ||||
| from django.test import SimpleTestCase, ignore_warnings | ||||
| from django.test.utils import override_settings | ||||
| from django.utils.deprecation import RemovedInDjango61Warning | ||||
|  | ||||
|  | ||||
| class CheckUrlConfigTests(SimpleTestCase): | ||||
| @@ -243,10 +244,11 @@ class CheckCustomErrorHandlersTests(SimpleTestCase): | ||||
|     @override_settings( | ||||
|         ROOT_URLCONF="check_framework.urls.bad_function_based_error_handlers", | ||||
|     ) | ||||
|     @ignore_warnings(category=RemovedInDjango61Warning) | ||||
|     def test_bad_function_based_handlers(self): | ||||
|         result = check_custom_error_handlers(None) | ||||
|         self.assertEqual(len(result), 4) | ||||
|         for code, num_params, error in zip([400, 403, 404, 500], [2, 2, 2, 1], result): | ||||
|         for code, error in zip([400, 403, 404, 500], result): | ||||
|             with self.subTest("handler{}".format(code)): | ||||
|                 self.assertEqual( | ||||
|                     error, | ||||
| @@ -254,9 +256,7 @@ class CheckCustomErrorHandlersTests(SimpleTestCase): | ||||
|                         "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 "" | ||||
|                         ), | ||||
|                         "(request, exception).".format(code), | ||||
|                         id="urls.E007", | ||||
|                     ), | ||||
|                 ) | ||||
| @@ -264,10 +264,11 @@ class CheckCustomErrorHandlersTests(SimpleTestCase): | ||||
|     @override_settings( | ||||
|         ROOT_URLCONF="check_framework.urls.bad_class_based_error_handlers", | ||||
|     ) | ||||
|     @ignore_warnings(category=RemovedInDjango61Warning) | ||||
|     def test_bad_class_based_handlers(self): | ||||
|         result = check_custom_error_handlers(None) | ||||
|         self.assertEqual(len(result), 4) | ||||
|         for code, num_params, error in zip([400, 403, 404, 500], [2, 2, 2, 1], result): | ||||
|         for code, error in zip([400, 403, 404, 500], result): | ||||
|             with self.subTest("handler%s" % code): | ||||
|                 self.assertEqual( | ||||
|                     error, | ||||
| @@ -275,11 +276,7 @@ class CheckCustomErrorHandlersTests(SimpleTestCase): | ||||
|                         "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 "", | ||||
|                         ), | ||||
|                         "arguments (request, exception)." % code, | ||||
|                         id="urls.E007", | ||||
|                     ), | ||||
|                 ) | ||||
| @@ -287,6 +284,7 @@ class CheckCustomErrorHandlersTests(SimpleTestCase): | ||||
|     @override_settings( | ||||
|         ROOT_URLCONF="check_framework.urls.bad_error_handlers_invalid_path" | ||||
|     ) | ||||
|     @ignore_warnings(category=RemovedInDjango61Warning) | ||||
|     def test_bad_handlers_invalid_path(self): | ||||
|         result = check_custom_error_handlers(None) | ||||
|         paths = [ | ||||
| @@ -318,6 +316,7 @@ class CheckCustomErrorHandlersTests(SimpleTestCase): | ||||
|     @override_settings( | ||||
|         ROOT_URLCONF="check_framework.urls.good_function_based_error_handlers", | ||||
|     ) | ||||
|     @ignore_warnings(category=RemovedInDjango61Warning) | ||||
|     def test_good_function_based_handlers(self): | ||||
|         result = check_custom_error_handlers(None) | ||||
|         self.assertEqual(result, []) | ||||
| @@ -325,6 +324,7 @@ class CheckCustomErrorHandlersTests(SimpleTestCase): | ||||
|     @override_settings( | ||||
|         ROOT_URLCONF="check_framework.urls.good_class_based_error_handlers", | ||||
|     ) | ||||
|     @ignore_warnings(category=RemovedInDjango61Warning) | ||||
|     def test_good_class_based_handlers(self): | ||||
|         result = check_custom_error_handlers(None) | ||||
|         self.assertEqual(result, []) | ||||
|   | ||||
| @@ -22,7 +22,8 @@ from django.middleware.csrf import ( | ||||
|     get_token, | ||||
|     rotate_token, | ||||
| ) | ||||
| from django.test import SimpleTestCase, override_settings | ||||
| from django.test import SimpleTestCase, ignore_warnings, override_settings | ||||
| from django.utils.deprecation import RemovedInDjango61Warning | ||||
| from django.views.decorators.csrf import csrf_exempt, requires_csrf_token | ||||
|  | ||||
| from .views import ( | ||||
| @@ -1477,6 +1478,7 @@ class CsrfViewMiddlewareUseSessionsTests(CsrfViewMiddlewareTestMixin, SimpleTest | ||||
|  | ||||
| @override_settings(ROOT_URLCONF="csrf_tests.csrf_token_error_handler_urls", DEBUG=False) | ||||
| class CsrfInErrorHandlingViewsTests(CsrfFunctionTestMixin, SimpleTestCase): | ||||
|     @ignore_warnings(category=RemovedInDjango61Warning) | ||||
|     def test_csrf_token_on_404_stays_constant(self): | ||||
|         response = self.client.get("/does not exist/") | ||||
|         # The error handler returns status code 599. | ||||
|   | ||||
| @@ -1,7 +1,13 @@ | ||||
| from django.core.exceptions import PermissionDenied | ||||
| from django.template.response import TemplateResponse | ||||
| from django.test import SimpleTestCase, modify_settings, override_settings | ||||
| from django.test import ( | ||||
|     SimpleTestCase, | ||||
|     ignore_warnings, | ||||
|     modify_settings, | ||||
|     override_settings, | ||||
| ) | ||||
| from django.urls import path | ||||
| from django.utils.deprecation import RemovedInDjango61Warning | ||||
|  | ||||
|  | ||||
| class MiddlewareAccessingContent: | ||||
| @@ -38,6 +44,7 @@ handler403 = template_response_error_handler | ||||
|     } | ||||
| ) | ||||
| class CustomErrorHandlerTests(SimpleTestCase): | ||||
|     @ignore_warnings(category=RemovedInDjango61Warning) | ||||
|     def test_handler_renders_template_response(self): | ||||
|         """ | ||||
|         BaseHandler should render TemplateResponse if necessary. | ||||
|   | ||||
| @@ -13,7 +13,13 @@ from django.contrib.auth.models import User | ||||
| from django.core.exceptions import ImproperlyConfigured, ViewDoesNotExist | ||||
| from django.http import HttpRequest, HttpResponsePermanentRedirect, HttpResponseRedirect | ||||
| from django.shortcuts import redirect | ||||
| from django.test import RequestFactory, SimpleTestCase, TestCase, override_settings | ||||
| from django.test import ( | ||||
|     RequestFactory, | ||||
|     SimpleTestCase, | ||||
|     TestCase, | ||||
|     ignore_warnings, | ||||
|     override_settings, | ||||
| ) | ||||
| from django.test.utils import override_script_prefix | ||||
| from django.urls import ( | ||||
|     NoReverseMatch, | ||||
| @@ -32,6 +38,7 @@ from django.urls import ( | ||||
|     reverse_lazy, | ||||
| ) | ||||
| from django.urls.resolvers import RegexPattern | ||||
| from django.utils.deprecation import RemovedInDjango61Warning | ||||
|  | ||||
| from . import middleware, urlconf_outer, views | ||||
| from .utils import URLObject | ||||
| @@ -1473,12 +1480,15 @@ class ErrorHandlerResolutionTests(SimpleTestCase): | ||||
|         self.resolver = URLResolver(RegexPattern(r"^$"), urlconf) | ||||
|         self.callable_resolver = URLResolver(RegexPattern(r"^$"), urlconf_callables) | ||||
|  | ||||
|     @ignore_warnings(category=RemovedInDjango61Warning) | ||||
|     def test_named_handlers(self): | ||||
|         for code in [400, 403, 404, 500]: | ||||
|             with self.subTest(code=code): | ||||
|                 self.assertEqual(self.resolver.resolve_error_handler(code), empty_view) | ||||
|  | ||||
|     @ignore_warnings(category=RemovedInDjango61Warning) | ||||
|     def test_callable_handlers(self): | ||||
|         # After Django 6.1 removal, only test with 'error_handler' and one code. | ||||
|         for code in [400, 403, 404, 500]: | ||||
|             with self.subTest(code=code): | ||||
|                 self.assertEqual( | ||||
|   | ||||
| @@ -4,7 +4,9 @@ from .views import empty_view | ||||
|  | ||||
| urlpatterns = [] | ||||
|  | ||||
| # RemovedInDjango61Warning: the handler<...> variables can be removed | ||||
| handler400 = empty_view | ||||
| handler403 = empty_view | ||||
| handler404 = empty_view | ||||
| handler500 = empty_view | ||||
| error_handler = empty_view | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| # A URLconf that doesn't define any handlerXXX. | ||||
| # A URLconf that doesn't define any error handler view. | ||||
| from django.urls import path | ||||
|  | ||||
| from .views import bad_view, empty_view | ||||
|   | ||||
		Reference in New Issue
	
	Block a user