How to test 500.html error page in django development env?

后端 未结 10 1192
情话喂你
情话喂你 2020-12-28 13:00

I am using Django for a project and is already in production.

In the production environment 500.html is rendered whenever a server error occurs.

How do I tes

相关标签:
10条回答
  • 2020-12-28 13:20

    In Django versions < 3.0, you should do as follows:

    client.py

    from django.core.signals import got_request_exception
    from django.template import TemplateDoesNotExist
    from django.test import signals
    from django.test.client import Client as DjangoClient, store_rendered_templates
    from django.urls import resolve
    from django.utils import six
    from django.utils.functional import SimpleLazyObject, curry
    
    
    class Client(DjangoClient):
        """Test client that does not raise Exceptions if requested."""
    
        def __init__(self, 
                     enforce_csrf_checks=False, 
                     raise_request_exception=True, **defaults):
            super(Client, self).__init__(enforce_csrf_checks=enforce_csrf_checks, 
                                         **defaults)
            self.raise_request_exception = raise_request_exception
    
        def request(self, **request):
            """
            The master request method. Composes the environment dictionary
            and passes to the handler, returning the result of the handler.
            Assumes defaults for the query environment, which can be overridden
            using the arguments to the request.
            """
            environ = self._base_environ(**request)
    
            # Curry a data dictionary into an instance of the template renderer
            # callback function.
            data = {}
            on_template_render = curry(store_rendered_templates, data)
            signal_uid = "template-render-%s" % id(request)
            signals.template_rendered.connect(on_template_render, 
                                              dispatch_uid=signal_uid)
            # Capture exceptions created by the handler.
            exception_uid = "request-exception-%s" % id(request)
            got_request_exception.connect(self.store_exc_info, 
                                          dispatch_uid=exception_uid)
            try:
                try:
                    response = self.handler(environ)
                except TemplateDoesNotExist as e:
                    # If the view raises an exception, Django will attempt to show
                    # the 500.html template. If that template is not available,
                    # we should ignore the error in favor of re-raising the
                    # underlying exception that caused the 500 error. Any other
                    # template found to be missing during view error handling
                    # should be reported as-is.
                    if e.args != ('500.html',):
                        raise
    
                # Look for a signalled exception, clear the current context
                # exception data, then re-raise the signalled exception.
                # Also make sure that the signalled exception is cleared from
                # the local cache!
                response.exc_info = self.exc_info  # Patch exception handling
                if self.exc_info:
                    exc_info = self.exc_info
                    self.exc_info = None
                    if self.raise_request_exception:  # Patch exception handling
                        six.reraise(*exc_info)
    
                # Save the client and request that stimulated the response.
                response.client = self
                response.request = request
    
                # Add any rendered template detail to the response.
                response.templates = data.get("templates", [])
                response.context = data.get("context")
    
                response.json = curry(self._parse_json, response)
    
                # Attach the ResolverMatch instance to the response
                response.resolver_match = SimpleLazyObject(
                    lambda: resolve(request['PATH_INFO'])
                )
    
                # Flatten a single context. Not really necessary anymore thanks to
                # the __getattr__ flattening in ContextList, but has some edge-case
                # backwards-compatibility implications.
                if response.context and len(response.context) == 1:
                    response.context = response.context[0]
    
                # Update persistent cookie data.
                if response.cookies:
                    self.cookies.update(response.cookies)
    
                return response
            finally:
                signals.template_rendered.disconnect(dispatch_uid=signal_uid)
                got_request_exception.disconnect(dispatch_uid=exception_uid)
    

    tests.py

    from unittest import mock
    
    from django.contrib.auth import get_user_model
    from django.core.urlresolvers import reverse
    from django.test import TestCase, override_settings
    
    from .client import Client  # Important, we use our own Client here!
    
    
    class TestErrors(TestCase):
        """Test errors."""
    
        @classmethod
        def setUpClass(cls):
            super(TestErrors, cls).setUpClass()
            cls.username = 'admin'
            cls.email = 'admin@localhost'
            cls.password = 'test1234test1234'
            cls.not_found_url = '/i-do-not-exist/'
            cls.internal_server_error_url = reverse('password_reset')
    
        def setUp(self):
            super(TestErrors, self).setUp()
            User = get_user_model()
    
            User.objects.create_user(
                self.username,
                self.email,
                self.password,
                is_staff=True,
                is_active=True
            )
            self.client = Client(raise_request_exception=False)
    
        # Mock in order to trigger Exception and resulting Internal server error
        @mock.patch('django.contrib.auth.views.PasswordResetView.form_class', None)
        @override_settings(DEBUG=False)
        def test_errors(self):
            self.client.login(username=self.username, password=self.password)
    
            with self.subTest("Not found (404)"):
                response = self.client.get(self.not_found_url, follow=True)
                self.assertNotIn('^admin/', str(response.content))
    
            with self.subTest("Internal server error (500)"):
                response = self.client.get(self.internal_server_error_url, 
                                           follow=True)
                self.assertNotIn('TypeError', str(response.content))
    
    

    Starting from Django 3.0 you could skip the custom Client definition and just use the code from tests.py.

    0 讨论(0)
  • 2020-12-28 13:21

    urls.py

    handler500 = 'project.apps.core.views.handler500'
    handler404 = 'project.apps.core.views.handler404'
    

    views.py

    from django.template.loader import get_template
    from django.template import Context
    from django.http import HttpResponseServerError, HttpResponseNotFound
    
    
    def handler500(request, template_name='500.html'):
        t = get_template(template_name)
        ctx = Context({})
        return HttpResponseServerError(t.render(ctx))
    
    
    def handler404(request, template_name='404.html'):
        t = get_template(template_name)
        ctx = Context({})
        return HttpResponseNotFound(t.render(ctx))
    

    tests.py

    from django.test import TestCase
    from django.test.client import RequestFactory
    
    from project import urls
    
    from ..views import handler404, handler500
    
    
    class TestErrorPages(TestCase):
    
        def test_error_handlers(self):
            self.assertTrue(urls.handler404.endswith('.handler404'))
            self.assertTrue(urls.handler500.endswith('.handler500'))
            factory = RequestFactory()
            request = factory.get('/')
            response = handler404(request)
            self.assertEqual(response.status_code, 404)
            self.assertIn('404 Not Found!!', unicode(response))
            response = handler500(request)
            self.assertEqual(response.status_code, 500)
            self.assertIn('500 Internal Server Error', unicode(response))
    
    0 讨论(0)
  • 2020-12-28 13:30

    Continuing shanyu's answer, in Django 1.3+ use:

    if settings.DEBUG:
        urlpatterns += patterns('',
            (r'^500/$', 'django.views.defaults.server_error'),
            (r'^404/$', 'django.views.defaults.page_not_found'),
        )
    
    0 讨论(0)
  • 2020-12-28 13:33

    And if you want to use the default Django 500 view instead of your custom view:

    if settings.DEBUG:
        urlpatterns += patterns('',
            (r'^500/$', 'django.views.defaults.server_error'),
            (r'^404/$', 'django.views.generic.simple.direct_to_template', {'template': '404.html'}),
        )
    
    0 讨论(0)
  • 2020-12-28 13:34

    In Django 1.6 django.views.generic.simple.direct_to_template does not exists anymore, these are my settings for special views:

    # urls.py
    
    from django.views.generic import TemplateView
    from django.views.defaults import page_not_found, server_error
    
    urlpatterns += [
        url(r'^400/$', TemplateView.as_view(template_name='400.html')),
        url(r'^403/$', TemplateView.as_view(template_name='403.html')),
        url(r'^404/$', page_not_found),
        url(r'^500/$', server_error),
    ]
    
    0 讨论(0)
  • 2020-12-28 13:37

    Update for Django > 1.6 and without getting

    page_not_found() missing 1 required positional argument: 'exception'
    

    Inspired by this answer:

    # urls.py
    from django.views.defaults import page_not_found, server_error, permission_denied, bad_request
    [...]
    if settings.DEBUG:
        # This allows the error pages to be debugged during development, just visit
        # these url in browser to see how these error pages look like.
        urlpatterns += [
        path('400/', bad_request, kwargs={'exception': Exception('Bad Request!')}),
        path('403/', permission_denied, kwargs={'exception': Exception('Permission Denied')}),
        path('404/', page_not_found, kwargs={'exception': Exception('Page not Found')}),
        path('500/', server_error),
    
    0 讨论(0)
提交回复
热议问题