私はプロジェクトにDjangoを使用しており、すでに本番環境にあります。
本番環境では、サーバーエラーが発生するたびに500.htmlがレンダリングされます。
開発環境で500.htmlのレンダリングをテストするにはどうすればよいですか?または、devで500.htmlをレンダリングするにはどうすればよいですか?デバッグをオフにしても、500.htmlではなくエラーが発生します
背景:ページに基づいていくつかのページ要素を含めましたが、500.htmlが呼び出されたときに欠落していて、開発環境でデバッグしたい場合があります。
DEBUGをオフにしたくない。代わりに、次のスニペットをurls.pyに配置します。
if settings.DEBUG:
urlpatterns += patterns('',
(r'^500/$', 'your_custom_view_if_you_wrote_one'),
(r'^404/$', 'Django.views.generic.simple.direct_to_template', {'template': '404.html'}),
)
上記のスニペットでは、エラーページはカスタムビューを使用していますが、Djangoのdirect_to_templateビューに簡単に置き換えることができます。
これで、URLを呼び出すことで500ページと404ページをテストできます: http://example.com/5 および http://example.com/404
In Django 1.6 Django.views.generic.simple.direct_to_template
はもう存在しません、これらは特別なビューのための私の設定です:
# 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),
]
また、カスタムビューの代わりにデフォルトのDjango 500ビューを使用する場合:
if settings.DEBUG:
urlpatterns += patterns('',
(r'^500/$', 'Django.views.defaults.server_error'),
(r'^404/$', 'Django.views.generic.simple.direct_to_template', {'template': '404.html'}),
)
継続 shanyuの答え 、Django 1.3+使用:
if settings.DEBUG:
urlpatterns += patterns('',
(r'^500/$', 'Django.views.defaults.server_error'),
(r'^404/$', 'Django.views.defaults.page_not_found'),
)
両方のデバッグ設定が間違っていますか?
settings.DEBUG = False
settings.TEMPLATE_DEBUG = False
カスタムエラーハンドラーを実行およびテストする方法
View
に基づいてカスタムTemplateView
を定義します
# views.py
from Django.views.generic import TemplateView
class ErrorHandler(TemplateView):
""" Render error template """
error_code = 404
template_name = 'index/error.html'
def dispatch(self, request, *args, **kwargs):
""" For error on any methods return just GET """
return self.get(request, *args, **kwargs)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['error_code'] = self.error_code
return context
def render_to_response(self, context, **response_kwargs):
""" Return correct status code """
response_kwargs = response_kwargs or {}
response_kwargs.update(status=self.error_code)
return super().render_to_response(context, **response_kwargs)
Djangoカスタムエラーハンドラーを使用するように指示する
# urls.py
from index.views import ErrorHandler
# error handing handlers - fly binding
for code in (400, 403, 404, 500):
vars()['handler{}'.format(code)] = ErrorHandler.as_view(error_code=code)
カスタムエラーハンドラーのテストケース
# tests.py
from unittest import mock
from Django.test import TestCase
from Django.core.exceptions import SuspiciousOperation, PermissionDenied
from Django.http import Http404
from index import views
class ErrorHandlersTestCase(TestCase):
""" Check is correct error handlers work """
def raise_(exception):
def wrapped(*args, **kwargs):
raise exception('Test exception')
return wrapped
def test_index_page(self):
""" Should check is 200 on index page """
response = self.client.get('/')
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'index/index.html')
@mock.patch('index.views.IndexView.get', raise_(Http404))
def test_404_page(self):
""" Should check is 404 page correct """
response = self.client.get('/')
self.assertEqual(response.status_code, 404)
self.assertTemplateUsed(response, 'index/error.html')
self.assertIn('404 Page not found', response.content.decode('utf-8'))
@mock.patch('index.views.IndexView.get', views.ErrorHandler.as_view(error_code=500))
def test_500_page(self):
""" Should check is 500 page correct """
response = self.client.get('/')
self.assertEqual(response.status_code, 500)
self.assertTemplateUsed(response, 'index/error.html')
self.assertIn('500 Server Error', response.content.decode('utf-8'))
@mock.patch('index.views.IndexView.get', raise_(SuspiciousOperation))
def test_400_page(self):
""" Should check is 400 page correct """
response = self.client.get('/')
self.assertEqual(response.status_code, 400)
self.assertTemplateUsed(response, 'index/error.html')
self.assertIn('400 Bad request', response.content.decode('utf-8'))
@mock.patch('index.views.IndexView.get', raise_(PermissionDenied))
def test_403_page(self):
""" Should check is 403 page correct """
response = self.client.get('/')
self.assertEqual(response.status_code, 403)
self.assertTemplateUsed(response, 'index/error.html')
self.assertIn('403 Permission Denied', response.content.decode('utf-8'))
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))
Djangoバージョン<3.0では、次のようにする必要があります。
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))
Django 3.0から、カスタムClient
定義をスキップして、tests.py
のコードを使用することができます。
この回答で詳しく説明されているように、メインのhandler404
ファイルのエラーに対してhandler500
とviews.py
を定義するだけです。
https://stackoverflow.com/a/18009660/1913888
これにより、Djangoがそのハンドラーにルーティングするときに必要なエラーが返されます。別のURL名にルーティングするために、カスタムURL構成は必要ありません。