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 full-size 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 full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size 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 full-size slide

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

    View full-size 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 full-size slide

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


    View full-size slide

  16. WSGI
    登場前

    View full-size slide

  17. WSGI
    登場後

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size 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 full-size slide

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

    View full-size 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 full-size slide

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

    View full-size slide

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

    View full-size 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 full-size slide

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


    View full-size 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 full-size 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 full-size 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 full-size 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 full-size 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 full-size slide

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

    View full-size slide

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


    View full-size slide

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


    View full-size 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 full-size 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 full-size 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 full-size 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 full-size slide

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

    View full-size 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 full-size 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 full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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


    View full-size slide

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

    View full-size slide

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

    View full-size 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 full-size 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 full-size 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 full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size 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 full-size slide

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

    View full-size 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 full-size slide

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

    View full-size 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 full-size 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 full-size slide

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

    View full-size slide

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

    View full-size 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 full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size 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 full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size 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 full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size 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 full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size 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 full-size 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 full-size slide

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

    View full-size slide

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

    View full-size 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 full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

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

    View full-size slide

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

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

    View full-size slide

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

    View full-size 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 full-size 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 full-size 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 full-size slide

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

    View full-size slide

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

    View full-size 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 full-size slide

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

    View full-size slide

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

    View full-size slide