Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Django・WSGIミドルウェア入門/django-congress-jp-2018-talk

 Django・WSGIミドルウェア入門/django-congress-jp-2018-talk

Django Congress JP 2018の発表資料です

thinkAmi

May 18, 2018
Tweet

More Decks by thinkAmi

Other Decks in Programming

Transcript

  1. Django/WSGI
    ミドルウェア入門
    (Django Congress JP 2018 talk)

    View Slide

  2. お前誰よ / Who are you ?
    thinkAmi
    Python & Django
    Blog
    :メモ的な思考的な (http://thinkami.hatenablog.com/)
    (
    株)
    日本システム技研
    PyCon JP 2016-17 Silver Sponsor
    ギークラボ長野
    Python Boot Camp in
    長野
    みんなのPython
    勉強会 in
    長野

    View Slide

  3. Django/WSGI
    ミドルウェア入門
    (Django Congress JP 2018 talk)

    View Slide

  4. 話すこと
    WSGI
    とは
    WSGI/Django
    ミドルウェアとは
    WSGI/Django
    ミドルウェアの実装
    Tips
    複数利用、例外ハンドリング、他

    View Slide

  5. 話す範囲
    Django
    のリクエスト/
    レスポンスの流れ

    View Slide

  6. 話さないこと
    デフォルトで設定されるDjango
    ミドルウェアについて
    すごいWSGI/Django
    ミドルウェアを作った話
    WSGI/Django
    ミドルウェアのベストプラクティス

    View Slide

  7. 注意
    このトークを聴いても、Django
    アプリは作れません
    ソースコードの一部は省略
    GitHub
    に全体を公開済
    https://github.com/thinkAmi/DjangoCongress_JP_2018_talk

    View Slide

  8. こんなことありませんか
    このページは、ログイン必須にしてほしい

    View Slide

  9. こんなことありませんか
    このページは、ログイン必須にしてほしい
    class FooView(LoginRequiredMixin, TemplateView):

    View Slide

  10. こんなことありませんか
    このページも、ログイン必須にしてほしい
    class FooView(LoginRequiredMixin, TemplateView):
    #
    増えた
    class BarView(LoginRequiredMixin, TemplateView):

    View Slide

  11. こんなことありませんか
    やっぱり、全部のページをログイン必須にしてほしい

    View Slide

  12. Django
    ミドルウェアを使う
    ログインしているかどうかの処理をまとめられた
    class LoginRequiredMiddleware:
    def process_view(self, request, view_func, view_args, view_kwargs):
    if not request.user.is_authenticated:
    return HttpResponse('permission denied!\n')

    View Slide

  13. WSGI
    WSGI
    とは
    WSGI
    アプリケーションとは
    WSGI
    サーバとは

    View Slide

  14. WSGI
    とは
    Python Web Server Gateway Interface (WSGI)
    PEP333
    とPEP3333
    PEP333 (Python2)
    PEP3333 (Python3
    対応)
    https://www.python.org/dev/peps/pep-3333/
    https://knzm.readthedocs.io/en/latest/pep-3333-ja.html

    View Slide

  15. WSGI
    とは
    Web
    サーバソフトウェアとPython
    で記述されたWeb
    アプリケーシ
    ョンとの標準インターフェース
    https://docs.python.jp/3/library/wsgiref.html


    View Slide

  16. WSGI
    登場前

    View Slide

  17. WSGI
    登場後

    View Slide

  18. WSGI
    アプリケーションとは

    View Slide

  19. WSGI
    アプリケーションとは
    2
    つの固定引数を持つアプリケーションオブジェクト
    environ
    CGI
    スタイルの環境変数を含む辞書オブジェクト
    start_response
    2
    つの固定引数(status
    ・response_headers)
    と、1
    つの任意引数
    (exc_info)
    を受け付ける、呼び出し可能なオブジェクト
    戻り値は、HTTP
    レスポンスボディ(iterable
    なバイト文字列)
    関数ベース・クラスベースの2
    種類

    View Slide

  20. 関数ベースのWSGI
    アプリ
    def hello_wsgi(environ, start_response):
    start_response('200 OK', [('Content-Type', 'text/plain')])
    return [b'Hello, WSGI function\n']

    View Slide

  21. クラスベースのWSGI
    アプリ
    class HelloWSGI:
    def __call__(self, environ, start_response):
    start_response('200 OK', [('Content-Type', 'text/plain')])
    return [b'Hello, WSGI class\n']

    View Slide

  22. WSGI
    登場前のDjango
    Apache + mod_python
    の組み合わせを推奨
    http://djangoproject.jp/doc/ja/1.0/howto/deployment/modpython
    .html

    View Slide

  23. WSGI
    登場後のDjango
    Django 1.5
    で、mod_python
    のサポートが削除
    http://django.readthedocs.io/en/1.4.X/howto/deployment/modpy
    thon.html
    Django
    の主要なデプロイプラットフォームは、 Web
    サーバとWeb
    アプリケーションに関してPython
    の標準であるWSGI
    です。
    https://docs.djangoproject.com/ja/2.0/howto/deployment/wsgi/


    View Slide

  24. Django
    のWSGI
    実装
    Django
    のソースコードを追ってみる
    WSGI_APPLICATION
    より
    デフォルトは your_project/wsgi.py
    application = get_wsgi_application()

    View Slide

  25. Django
    とWSGI
    django.core.wsgi.py
    def get_wsgi_application():
    ...
    return WSGIHandler()

    View Slide

  26. Django
    とWSGI
    django.core.handlers.wsgi.py
    class WSGIHandler(base.BaseHandler):
    def __call__(self, environ, start_response):
    ...
    start_response(status, response_headers)
    ...
    return response
    Django
    はクラスベース

    View Slide

  27. WSGI
    サーバとは
    uWSGI
    Gunicorn
    mod_wsgi
    WSGI
    サーバ(WSGI
    アプリケーションコンテナ)は、WSGI
    アプリ
    ケーションを常駐させ、HTTP
    クライアントからリクエストを受け
    取るごとに、WSGI
    アプリケーションのcallable
    オブジェクトを呼び
    出す
    https://ja.wikipedia.org/wiki/Web_Server_Gateway_Interface


    View Slide

  28. WSGI
    アプリとWSGI
    サーバ
    WSGI
    アプリを動かすためには、WSGI
    サーバが必要
    標準モジュール wsgiref
    のWSGI
    サーバ
    wsgiref.simple_server.make_server
    Django
    の開発サーバも、標準モジュール wsgiref
    を使用
    $ python manage.py runserver
    django.core.management.commands.runserver.py
    django.core.servers.basehttp.py

    View Slide

  29. 関数ベースのWSGI
    アプリを動かす
    WSGI
    サーバにWSGI
    アプリを乗せる
    from wsgiref.simple_server import make_server
    from wsgi_function import hello_wsgi
    if __name__ == '__main__':
    server = make_server('', 15000, hello_wsgi)
    server.serve_forever()

    View Slide

  30. 関数ベースのWSGI
    アプリを動かす
    WSGI
    アプリ
    def hello_wsgi(environ, start_response):
    return [b'Hello, WSGI function\n']
    WSGI
    サーバを起動後、確認
    $ curl http://localhost:15000
    Hello, WSGI function

    View Slide

  31. クラスベースのWSGI
    アプリを動かす
    WSGI
    サーバにWSGI
    アプリを乗せる
    from wsgiref.simple_server import make_server
    from wsgi_class import HelloWSGI
    if __name__ == '__main__':
    server = make_server('', 15001, HelloWSGI())
    server.serve_forever()

    View Slide

  32. クラスベースのWSGI
    アプリを動かす
    WSGI
    アプリ
    class HelloWSGI:
    def __call__(self, environ, start_response):
    return [b'Hello, WSGI class\n']
    WSGI
    サーバを起動後、確認
    $ curl http://localhost:15001
    Hello, WSGI class

    View Slide

  33. WSGI
    ミドルウェア
    WSGI
    ミドルウェアとは
    WSGI
    ミドルウェアを実装
    WSGI
    ミドルウェアをDjango
    へ組み込み

    View Slide

  34. ミドルウェアとは
    Web
    サーバ
    アプリケーションサーバ
    データベースサーバ
    コンピュータの基本的な制御を行うオペレーティングシステム(OS)
    と、各業務処理を行うアプリケーションソフトウェアとの中間に入
    るソフトウェアのこと
    https://ja.wikipedia.org/wiki/
    ミドルウェア


    View Slide

  35. WSGI
    ミドルウェアとは
    Beaker
    wsgi_lineprof
    サーバとWSGI
    アプリケーションの両方のインターフェースを持つ
    オブジェクト
    Web
    サーバ側からはWSGI
    アプリケーションのように見え,WSGI
    アプリケーション側からはWeb
    サーバのように見える
    http://gihyo.jp/dev/feature/01/wsgi/0003


    View Slide

  36. WSGI
    ミドルウェア
    class WSGIMiddleware:
    def __init__(self, app):
    self.app = app
    def __call__(self, environ, start_response):
    # WSGI
    アプリの処理前に、何か処理をする場所
    response = self.app(environ, start_response)
    # WSGI
    アプリの処理後に、何か処理をする場所
    return response

    View Slide

  37. (
    例)
    レスポンスを追加するミドルウェア
    class HelloWSGIMiddleware:
    def __init__(self, app):
    self.app = app
    def __call__(self, environ, start_response):
    response = self.app(environ, start_response)
    response[0] += b'Hello, WSGI middleware\n'
    return response

    View Slide

  38. WSGI
    ミドルウェア + WSGI
    アプリを動かす
    WSGI
    アプリ + WSGI
    ミドルウェアを、WSGI
    サーバで動かす
    from wsgiref.simple_server import make_server
    from wsgi_class import HelloWSGI
    from wsgi_middleware import HelloWSGIMiddleware
    if __name__ == '__main__':
    wsgi_app = HelloWSGI()
    app_with_middleware = HelloWSGIMiddleware(wsgi_app)
    server = make_server('', 15002, app_with_middleware)
    server.serve_forever()

    View Slide

  39. WSGI
    ミドルウェア + WSGI
    アプリを動かす
    WSGI
    アプリ
    class HelloWSGI:
    def __call__(self, environ, start_response):
    return [b'Hello, WSGI class\n']
    WSGI
    サーバを起動後、確認
    $ curl http://localhost:15002
    Hello, WSGI class
    Hello, WSGI middleware

    View Slide

  40. Django
    にWSGI
    ミドルウェアを組み込む

    View Slide

  41. Django
    にWSGI
    ミドルウェアを組み込む
    Django
    アプリのView
    # `localhost:8000/myapp/hello`
    で呼ばれるView
    class HelloView(View):
    def get(self, request, *args, **kwargs):
    print('called: HelloView')
    return HttpResponse('Hello world\n')

    View Slide

  42. Django
    にWSGI
    ミドルウェアを組み込む
    WSGI
    ミドルウェア
    class HelloWSGIMiddleware:
    def __init__(self, app):
    self.app = app
    def __call__(self, environ, start_response):
    print('[wsgi_middleware] before view')
    response = self.app(environ, start_response)
    print('[wsgi_middleware] after view')
    return response

    View Slide

  43. Django
    にWSGI
    ミドルウェアを組み込む
    wsgi.py
    に組み込み
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "myproject.settings")
    application = get_wsgi_application()
    #
    追加
    application = HelloWSGIMiddleware(application)

    View Slide

  44. 実行結果
    # curl
    結果
    $ curl localhost:8000/myapp/hello
    Hello world
    # Django
    ログ
    [wsgi_middleware] before view
    called: HelloView
    [wsgi_middleware] after view

    View Slide

  45. Django
    ミドルウェア
    Django
    ミドルウェアとは
    Django
    ミドルウェアを実装
    Django
    ミドルウェアによるフック

    View Slide

  46. Django
    ミドルウェアとは
    関数ベースとクラスベース
    Django
    のリクエスト/
    レスポンス処理にフックを加えるためのフレ
    ームワーク
    Django
    の入力あるいは出力をグローバルに置き換えるための、軽
    量で低レベルの「プラグイン」システム
    https://docs.djangoproject.com/ja/2.0/topics/http/middleware/


    View Slide

  47. Django
    ミドルウェアの全体像

    View Slide

  48. Django
    ミドルウェアを実装

    View Slide

  49. Django
    ミドルウェア
    class SimpleMiddleware:
    def __init__(self, get_response):
    self.get_response = get_response
    def __call__(self, request):
    # Django
    アプリの処理前に、何か処理をする場所
    response = self.get_response(request)
    # Django
    アプリの処理後に、何か処理をする場所
    return response
    https://docs.djangoproject.com/ja/2.0/topics/http/middleware/#writin
    g-your-own-middleware

    View Slide

  50. Django
    ミドルウェアを使ってみる
    Django
    アプリのView (
    再掲)
    # `localhost:8000/myapp/hello`
    で呼ばれるView
    class HelloView(View):
    def get(self, request, *args, **kwargs):
    print('called: HelloView')
    return HttpResponse('Hello world\n')

    View Slide

  51. Django
    ミドルウェアを使ってみる
    ミドルウェアの実装
    class HelloDjangoMiddleware:
    def __init__(self, get_response):
    self.get_response = get_response
    print('[hello] one-time configuration')
    def __call__(self, request):
    print('[hello] before view')
    response = self.get_response(request)
    print('[hello] after view')
    return response

    View Slide

  52. Django
    ミドルウェアを組み込む
    settings
    にて設定
    MIDDLEWARE = [
    'myproject.middlewares.hello_middleware.HelloDjangoMiddleware',
    ]

    View Slide

  53. 動作確認
    起動時
    $ python manage.py runserver --settings=myproject.settings.hello
    ...
    [hello] one-time configuration

    View Slide

  54. 動作確認
    curl
    でアクセスした時のDjango
    ログ
    [hello] before view
    called: HelloView
    [hello] after view

    View Slide

  55. Django
    ミドルウェアによるフック
    process_view()
    process_template_response()
    process_exception()

    View Slide

  56. process_view()
    Django
    がビューを呼び出す直前のフック
    https://github.com/django/django/blob/2.0.5/django/core/handle
    rs/base.py#L118
    request
    ・view_func
    などを、引数として受け取る

    View Slide

  57. process_view()
    の戻り値
    None
    か HttpResponse
    オブジェクト
    None
    の場合、他の process_view()
    へ遷移
    メソッドの戻り値が無い場合も同じ
    HttpResponse
    オブジェクトの場合、そのオブジェクトに差し替わる
    render()
    メソッドを持つオブジェクトを返す場合、
    process_template_response()
    が呼ばれる

    View Slide

  58. process_view()
    を実装したミドルウェア
    (
    例)
    クエリ文字列に foo
    が無い場合、HttpResponse
    を返す
    class ProcessViewDjangoMiddleware:
    ...
    def process_view(self, request, view_func, view_args, view_kwargs):
    if not request.GET.get('foo'):
    return HttpResponse('overwrite by process_view\n')

    View Slide

  59. 結果確認
    クエリ文字列にfoo
    があるリクエストの実行結果
    クエリ文字列にfoo
    が無いリクエストの実行結果

    View Slide

  60. クエリ文字列にfoo
    があるリクエストの実行結果
    curl
    結果
    $ curl localhost:8000/myapp/hello?foo=123
    Hello world
    Django
    ログ
    [process_view] before view
    [process_view] hook!
    called: HelloView # View
    が呼ばれている
    [process_view] after view

    View Slide

  61. クエリ文字列にfoo
    が無いリクエストの実行結果
    curl
    結果
    $ curl localhost:8000/myapp/hello?bar=456
    overwrite by process_view #
    ミドルウェアで差し替わった
    Django
    ログ
    [process_view] before view
    [process_view] hook!
    [process_view] after view # View
    が呼ばれていない

    View Slide

  62. process_template_response()
    View
    の実行直後のフック
    render()
    メソッドを持つレスポンスのみをフック
    TemplateResponse
    オブジェクトなどを、引数として受け取る
    https://github.com/django/django/blob/2.0.5/django/core/handlers/
    base.py#L144

    View Slide

  63. process_template_response()
    の戻り値
    render()
    メソッドを持つオブジェクト
    django.template.response.TemplateResponse
    など

    View Slide

  64. 使用するView
    とTemplate
    View
    class HelloTemplateView(TemplateView):
    template_name = 'hello.html'
    extra_context = {'message': 'hello'}
    def get(self, request, *args, **kwargs):
    return super().get(request, args, kwargs)
    Template
    {{ message }}

    View Slide

  65. process_template_response()
    を実装
    class ProcessTemplateResponseDjangoMiddleware:
    ...
    def process_template_response(self, request, response):
    response.context_data['message'] = 'overwrite by middleware'
    return response

    View Slide

  66. 結果確認
    HttpResponse
    を返すView
    の場合
    TemplateResponse
    を返すView
    の場合

    View Slide

  67. HttpResponse
    を返すView
    の場合
    View
    class HelloView(View):
    return HttpResponse('Hello world\n')
    実行結果
    $ curl localhost:8000/myapp/hello
    Hello world

    View Slide

  68. TemplateResponse
    を返すView
    の場合
    View
    class HelloTemplateView(TemplateView):
    extra_context = {'message': 'hello'}
    実行結果
    $ curl localhost:8000/myapp/template
    overwrite by middleware #
    値が差し替わった

    View Slide

  69. process_exception()
    View
    がException
    を投げたときのフック
    request
    やView
    の例外を、引数として受け取る
    https://github.com/django/django/blob/2.0.5/django/core/handlers/
    base.py#L128

    View Slide

  70. process_exception()
    の戻り値
    None
    か HttpResponse
    オブジェクト
    None
    の場合、他の process_exception()
    へ遷移
    メソッドの戻り値が無い場合も同じ
    HttpResponse
    オブジェクトの場合、そのオブジェクトに差し替わる
    render()
    メソッドを持つオブジェクトを返す場合、
    process_template_response()
    が呼ばれる

    View Slide

  71. 使用するView
    # `localhost:8000/myapp/error`
    で呼ばれるView
    class ExceptionView(View):
    def get(self, request, *args, **kwargs):
    raise ValueError('Oops!')

    View Slide

  72. process_exception()
    を実装したミドルウェア
    class ProcessExceptionDjangoMiddleware:
    def process_exception(self, request, exception):
    if request.GET.get('http'):
    return HttpResponse('HttpResponse by process_exception\n')
    elif request.GET.get('template'):
    return HelloTemplateView(request=request).render_to_response(
    {'message': 'TemplateResponse by process_exception\n'})
    def process_template_response(self, request, response):
    response.context_data['message'] += \
    'called process_template_response'
    return response

    View Slide

  73. 動作確認
    HttpResponse
    オブジェクトを返す場合
    クエリ文字列 http
    あり
    render()
    を持つオブジェクトを返す場合
    クエリ文字列 template
    あり
    None
    を返す場合
    いずれのクエリ文字列も無い

    View Slide

  74. HttpResponse
    オブジェクトを返す場合
    $ curl localhost:8000/myapp/error?http=0
    HttpResponse by process_exception

    View Slide

  75. render()
    を持つオブジェクトを返す場合
    $ curl localhost:8000/myapp/error?template=0
    TemplateResponse by process_exception
    called process_template_response

    View Slide

  76. None
    を返す場合
    curl
    の結果
    $ curl localhost:8000/myapp/error
    ...
    ValueError at /myapp/error
    ...
    Django
    のログ
    Internal Server Error: /myapp/error
    Traceback (most recent call last):
    ...
    raise ValueError('Oops!')
    ValueError: Oops!

    View Slide

  77. Tips
    WSGI/Django
    ミドルウェアの複数利用
    ミドルウェアでの例外送出
    Django1.9
    以前のミドルウェアのアップグレード
    Django
    ミドルウェアの動的解除

    View Slide

  78. ミドルウェアの複数利用

    View Slide

  79. WSGI
    ミドルウェアの複数利用

    View Slide

  80. WSGI
    ミドルウェアの設定
    Django View
    に近いWSGI
    ミドルウェアから設定
    wsgi.py
    application = ThirdWSGIMiddleware(application)
    application = SecondWSGIMiddleware(application)
    application = FirstWSGIMiddleware(application)

    View Slide

  81. 各WSGI
    ミドルウェア
    class FirstWSGIMiddleware:
    def __init__(self, app):
    self.app = app
    def __call__(self, environ, start_response):
    print('[wsgi1] before view')
    response = self.app(environ, start_response)
    print('[wsgi1] after view')
    return response

    View Slide

  82. 実行結果
    [wsgi1] before view
    [wsgi2] before view
    [wsgi3] before view
    called: HelloTemplateView
    [wsgi3] after view
    [wsgi2] after view
    [wsgi1] after view

    View Slide

  83. Django
    ミドルウェアの複数利用

    View Slide

  84. Django
    ミドルウェアの設定
    MIDDLEWARE = [
    'myproject.middlewares.multi_middleware.FirstDjangoMiddleware',
    'myproject.middlewares.multi_middleware.SecondDjangoMiddleware',
    'myproject.middlewares.multi_middleware.ThirdDjangoMiddleware',
    ]

    View Slide

  85. 各Django
    ミドルウェア
    class FirstDjangoMiddleware:
    def __init__(self, get_response):
    print('[django1] one-time configuration')
    def __call__(self, request):
    print('[django1] before view')
    response = self.get_response(request)
    print('[django1] after view')
    def process_view(self, request, view_func, view_args, view_kwargs):
    print('[django1] process view')
    def process_template_response(self, request, response):
    print('[django1] process template response')

    View Slide

  86. 結果
    [django3] one-time configuration
    [django2] one-time configuration
    [django1] one-time configuration
    [django1] before view
    [django2] before view
    [django3] before view
    [django1] process view
    [django2] process view
    [django3] process view
    called: HelloTemplateView
    [django3] process template response
    [django2] process template response
    [django1] process template response
    [django3] after view
    [django2] after view
    [django1] after view

    View Slide

  87. Django/WSGI
    ミドルウェアの複数利用

    View Slide

  88. 各ミドルウェアの設定
    WSGI
    application = SecondWSGIMiddleware(application)
    application = FirstWSGIMiddleware(application)
    Django
    MIDDLEWARE = [
    'myproject.middlewares.multi_middleware.FirstDjangoMiddleware',
    'myproject.middlewares.multi_middleware.SecondDjangoMiddleware',
    ]

    View Slide

  89. 結果
    [django2] one-time configuration
    [django1] one-time configuration
    [wsgi1] before view
    [wsgi2] before view
    [django1] before view
    [django2] before view
    [django1] process view
    [django2] process view
    called: HelloTemplateView
    [django2] process template response
    [django1] process template response
    [django2] after view
    [django1] after view
    [wsgi2] after view
    [wsgi1] after view

    View Slide

  90. ミドルウェアでの例外送出

    View Slide

  91. WSGI
    ミドルウェアで例外を送出
    WSGI
    ミドルウェアでのハンドリング
    Django
    ミドルウェアでのハンドリング

    View Slide

  92. WSGI
    ミドルウェアでのハンドリング
    request/response
    に近いWSGI
    ミドルウェアで、ハンドリングする
    application = RaiseExceptionWSGIMiddleware(application)
    application = HandlingExceptionWSGIMiddleware(application)

    View Slide

  93. Django
    ミドルウェアでのハンドリング
    Django
    ミドルウェアでは、WSGI
    例外をハンドリングできない
    リクエスト時:Django
    ミドルウェアが動作する前に、WSGI
    ミド
    ルウェアが動作するため
    レスポンス時:Django
    ミドルウェアが動作した後に、WSGI
    ミド
    ルウェアが動作するため

    View Slide

  94. Django
    ミドルウェアで例外を送出
    WSGI
    ミドルウェアでのハンドリング
    Django
    ミドルウェアでのハンドリング

    View Slide

  95. WSGI
    ミドルウェアでのハンドリング
    Django
    ミドルウェアの例外は、 django.http.response.HttpResponse

    ブジェクトに変換済
    例外そのものは、ハンドリングできない
    Django
    の世界のオブジェクトなので、WSGI
    ミドルウェアで処理する
    には不適切

    View Slide

  96. Django
    ミドルウェアでのハンドリング
    process_exception()
    では、例外をハンドリングできない
    上位ミドルウェアの self.get_response(request)
    に HttpResponse
    が返

    HTTP
    ステータスコード 500
    など
    MIDDLEWARE = [
    'myproject.xxx.HandlingExceptionDjangoMiddleware',
    'myproject.xxx.RaisingExceptionInProcessViewDjangoMiddleware',
    ]

    View Slide

  97. Django1.9
    以前の
    ミドルウェアのアップグレード

    View Slide

  98. Django1.9
    と1.10
    の違い
    1.10
    から、Django
    ミドルウェアの実装方法が変更
    1.9
    までの実装のままでは、TypeError: object() takes no parameters
    主な変更点
    MIDDLEWARE_CLASSES
    ではなく、MIDDLEWARE
    を使う
    __init__()
    メソッドに引数が必要
    __call__()
    メソッドが必要
    process_request()
    と process_response()
    がDjango2.0
    で削除
    https://docs.djangoproject.com/en/1.10/releases/1.10/#new-
    style-middleware

    View Slide

  99. アップグレード方法
    django.utils.deprecation.MiddlewareMixin
    を継承
    process_request()
    と process_response()
    を呼ぶMixin
    https://docs.djangoproject.com/ja/2.0/topics/http/middleware/#
    upgrading-pre-django-1-10-style-middleware

    View Slide

  100. MiddlewareMixin
    を使ったミドルウェア
    class DeprecationMixinDjangoMiddleware(MiddlewareMixin):
    def process_request(self, request):
    print('[mixin] process_request')
    def process_response(self, request, response):
    print('[mixin] process_response')
    return response
    def process_template_response(self, request, response):
    print('[mixin] process_template_response')
    response.context_data['message'] = 'overwrite by deprecation mixin middleware'
    return response

    View Slide

  101. 実行結果
    [mixin] process_request
    called: HelloTemplateView
    [mixin] process_template_response
    [mixin] process_response

    View Slide

  102. Django
    ミドルウェアの動的解除

    View Slide

  103. Django
    ミドルウェアの動的解除
    __init__()
    の中で、 django.core.exceptions.MiddlewareNotUsed
    を送出
    class UnusedDjangoMiddleware:
    def __init__(self, get_response):
    self.get_response = get_response
    raise MiddlewareNotUsed
    def process_template_response(self, request, response):
    response.context_data['message'] = 'overwrite by unused'
    return response

    View Slide

  104. 実行結果
    curl
    $ curl localhost:8000/myapp/template
    hello
    Django
    ログ
    [unused] one-time configuration
    called: HelloTemplateView

    View Slide

  105. 終わりに
    Django
    では、WSGI
    ミドルウェアとDjango
    ミドルウェアが使える
    Django
    ミドルウェアには、フックするポイントがいくつかある
    Enjoy, middleware life !!

    View Slide