LoginSignup
35
37

More than 3 years have passed since last update.

Django Server Error (500)攻略法【2019 アドカレ】

Last updated at Posted at 2019-11-30

アドベント・カレンダー初参加です。よろしくお願いしまーす。

概要

趣味 Pythonista です。今年 Django に入門し、ウェブサイトを作ってみました。今回は自分とおなじ Django 初心者に向けて、最初に頭を悩ませ悲痛な叫びを上げるであろうコトについて攻略法をざっくり書いてみようとおもいます。ズバリ Server Error (500)です。

Server Error (500)

このとき、一時的にでも画面にエラー内容を出したり、ログを出力したり、はたまた Slack にエラー内容を通知したりしてみます。

Server Error (500)発生

初心者なので views.py のメソッドについついバグを仕込んでしまいました。

views.py
def index(request):

    aaaa  # ←突然の aaaa

    return HttpResponse('<p>Hello world.</p>')

settings.py の DEBUGTrue だと、次のようにわかりやすいエラーメッセージが表示されます。

わかりやすいエラーメッセージ

ただし DEBUGFalse に変えた途端に親の顔より見た Server Error (500)が現れると思います。

Server Error (500)

この画面を出しているのは handler500

サーバエラーが発生したときのハンドラーが django.conf.urls.__init__.py にありました。

django/conf/urls/__init__.py
handler500 = defaults.server_error

サーバでエラーが起こると、この handler500 が実行されます。つまり defaults.server_error 関数が実行されます。 django.views.defaults.server_error の中身を覗くと、この画面を出している処理が記述されていますね。

django/views/defaults.py
@requires_csrf_token
def server_error(request, template_name=ERROR_500_TEMPLATE_NAME):
    """
    500 error handler.
    Templates: :template:`500.html`
    Context: None
    """

    # 中略

        return HttpResponseServerError(
            ERROR_PAGE_TEMPLATE % {'title': 'Server Error (500)', 'details': ''},
            content_type='text/html',
        )

だったら handler500 に代入する関数を defaults.server_error ではなく違う関数にしてしまえばいいんですね。

handler500 に違う関数を代入する

  1. 自前の server_error 関数を定義
  2. handler500 にそれを代入

自前の server_error は views.py に定義してみます。オリジナルのやつが django.views.defaults に定義してあるので。

views.py
@requires_csrf_token
def my_customized_server_error(request, template_name='500.html'):
    return HttpResponseServerError('<h1>Server Error (500)だよー</h1>')

handler500 への代入は urls.py で行います。オリジナルの handler500 の定義は django.conf.urls にあるので。

urls.py
handler500 = views.my_customized_server_error

Server Error 画面の変更をすることができました。

django3.jpg

あとはもう、 my_customized_server_error に好きなこと書けばいいですね。

my_customized_server_error の例

普通に print するコードです。

views.py
@requires_csrf_token
def my_customized_server_error(request, template_name='500.html'):
    import traceback
    print(traceback.format_exc())
    return HttpResponseServerError('<h1>Server Error (500)だよー</h1>')

DEBUG = True のときと同じエラー画面を出すコードです。これ見やすいですよね。(小並感)

views.py
@requires_csrf_token
def my_customized_server_error(request, template_name='500.html'):
    import sys
    from django.views import debug
    error_html = debug.technical_500_response(request, *sys.exc_info()).content
    return HttpResponseServerError(error_html)

親の顔より見たデフォルトの Server Error (500)画面を出すコードです。先程見つけ出した django.views.defaults.server_error を使えば OK です。

views.py
@requires_csrf_token
def my_customized_server_error(request, template_name='500.html'):
    from django.views.defaults import server_error
    return server_error(request, template_name)

みんな大好き Slack に通知を飛ばすコードです。

views.py
@requires_csrf_token
def my_customized_server_error(request, template_name='500.html'):
    import requests
    import json
    import traceback
    requests.post(
        'あなたの Slack Webhook URL',
        data=json.dumps({
            'text': '\n'.join([
                f'Request uri: {request.build_absolute_uri()}',
                traceback.format_exc(),
            ]),
            'username': 'Django エラー通知',
            'icon_emoji': ':jack_o_lantern:',
        })
    )
    return HttpResponseServerError('<h1>Server Error (500)だよー</h1>')

飛びました。私のぱそこのディレクトリ構成が表示されてしまうので、モザイクを入れさせてもらっちゃいます。

django4.jpg

明日の Django アドベント・カレンダーは @boxboxbax さんです。がんばってください!

35
37
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
35
37