Make組ブログ

Python、Webアプリや製品・サービス開発についてhirokikyが書きます。

WSGIからDjangoの流れを理解する

WSGIからの流れを大まかに掴めたのでまとめたい。

かなり大まかには、この画像を念頭におけばいいと思う。
今回はDjango1.4を対象にしている。

注意

流れを理解するために読むべきソースコードへのリンクと、その順番をまとめてる。
読みやすさはあんまり考慮してないし、細かな解説はしてない。
ソースコードの読むべき順番をメモっておきたいだけ。

DjangoのView

まず簡単に触れておきたいのがDjangoのView。

Viewとは

  • リクエストを受け取る
  • レスポンスを返す
  • 呼び出し可能である

オブジェクトのことで、このViewがリクエスト->レスポンスの末端となっている。
冒頭の図がわかりやすいと思う。

あとはチュートリアル
はじめてのビュー作成

クラスベースビューの場合、流れが複雑になるからそれはそれで別の機会にまとめたい。
https://github.com/django/django/blob/1.4/django/views/generic/base.py#L11

Djangoのhandler

Djangoの流れを理解するにはhandlerを読むと(・∀・)イイ!!
https://github.com/django/django/blob/1.4/django/core/handlers/base.py

BaseHandlerクラスのget_response()メソッドが流れの肝になる。
このメソッドはリクエストを受けとってレスポンスを返している。

handlerがしているのは

こんなところ。

適切なViewにリクエストを渡す

適切なViewにリクエストを渡すってのがURLDispatcherとかそのあたり。
https://github.com/django/django/blob/1.4/django/core/handlers/base.py#L98
このへん読めばいい。

実際にViewが呼び出されているのはここ。
https://github.com/django/django/blob/1.4/django/core/handlers/base.py#L111

Viewから返ってきたレスポンスのレンダリングを呼び出す

冒頭の図では、ビューからのレスポンスの矢印にTemplateが突き刺さってるけど、
あれはhandlerによって「あのへん」でTemplateがレンダリングされているからそう書いた。
具体的にはここ
https://github.com/django/django/blob/1.4/django/core/handlers/base.py#L136
強調したいのが、「View内ではTemplateのレンダリングが行われていない」ということ。
Viewはあくまで表示させたいデータを選択、提示しているのであって、表現はおこなっていない。
表現を担当しているのはTemplateであり、それがレンダリングされるのはhandlerのなか。
これはDjangoのルースカップリングの思想があるからこそ。

間でミドルウェアを呼び出す

まぁ読めばわかるけど、例えばrequest_middlewareが呼ばれてるのはここ。
https://github.com/django/django/blob/1.4/django/core/handlers/base.py#L88
たしかにViewの呼び出しの前にあるね。

WSGIHandler

んーでもってこのhandlerをWSGI対応させたのがWSGIHandler
https://github.com/django/django/blob/1.4/django/core/handlers/wsgi.py

そもそもWSGIって何よ。

WSGIとは

WebサーバとWebアプリケーションをつなげるインタフェースです。
http://ja.wikipedia.org/wiki/Web_Server_Gateway_Interface
だいたいはこれを読めばいい。

ここで言いたいのは、アプリケーション側が提供するのは呼び出し可能オブジェクトであればいいということ。

このオブジェクト(注: アプリケーション側が提供するオブジェクトのこと) が呼び出される際、引数 environ としてCGIと同じ環境変数が渡され、引数 start_response として、ステータスコードとレスポンスヘッダを受け取る呼び出し可能オブジェクトが渡される。

とのこと。
そんでもって

WSGIアプリケーションの戻り値は、本文を生成するイテレート可能なオブジェクトである必要がある。

結果はiteratableなオブジェクトを返せば良い、と。

WSGIHandlerを読む

__call__()読めばいいね。

  def __call__(self, environ, start_response):

たしかに environ と start_response を受け取ってる。
んでレスポンスを返してる。レスポンスというのはHttpResponseとかそのへんだろう。
(後述するけど、WSGIの仕様から言ってHttpResponseはiteratableなはず)

WSGIHandlerの入り口

エントリーポイントはここになってる。
https://github.com/django/django/blob/1.4/django/core/wsgi.py

まぁこれがプロジェクト配下のwsgi.pyから呼ばれるというわけ
https://github.com/django/django/blob/1.4/django/conf/project_template/project_name/wsgi.py

ちょっとWSGIの実装読もうか

http://hg.python.org/cpython/file/3f739f42be51/Lib/wsgiref/handlers.py

BaseHandlerクラスを読めばいい。
run() の中でアプリケーションが走り始めてるのが分かる。

アプリケーション側から返される『self.result』。
これはwrite()で使われているよう。
このwrite()によってサーバー側へコンテンツを返してるっぽい(まだちゃんと読んでない)。
(アプリケーションからの結果をStringIO的にφ(`д´)カキカキしてるんだろう)

HttpResponseとWSGI

self.resultはiteratableでないといけない、これはWSGIの仕様といった。
self.resultはDjangoのHttpResponseであるわけで、こいつは __iter__()でコンテンツを返すと予想できる。
読んでみる。

HttpResponseはこれ。
https://github.com/django/django/blob/1.4/django/http/__init__.py#L559
__iter__()はここに。
https://github.com/django/django/blob/1.4/django/http/__init__.py#L705

    def __iter__(self):
        self._iterator = iter(self._container)
        return self

self._containerというのをイテレータで返している。

self._containerについてはself.contentをみれば大体分かる

@content.setter
    def content(self, value):
        if hasattr(value, '__iter__') and not isinstance(value, (bytes, six.string_types)):
            self._container = value
            self._base_content_is_iter = True
        else:
            self._container = [value]
            self._base_content_is_iter = False

content -> _conteiner -> __iter__ -> wsgiref.base.BaseHandler.result
といった流れ。

まとめ

WSGI -> WSGIHandler -> View

いずれちゃんとまとめるかもしれない。