.. _topics-cache: ================================= Django のキャッシュフレームワーク ================================= :revision-up-to: 17812 (1.4) 動的な Web サイトの根本的なトレードオフ要因とは、まさに動的であるということ そのものです。ユーザがページをリクエストするたびに、サーバはデータベースへ のクエリからテンプレートのレンダリングやビジネスロジックといった全ての計算 を実行して、サイト訪問者が見るページを生成します。これは、処理のオーバヘッ ドという観点から考えると、ファイルシステムからファイルを読み出すタイプの標 準的なサーバ設計よりもはるかに高くつきます。 ほとんどの Web アプリケーションでは、このオーバヘッドはたいしたものではあり ません。ほとんどの Web アプリケーションは washingtonpost.com や slashdot.org とは違って、小規模から中規模のサイトであり、トラフィックもそこ そこにすぎません。しかし、中規模以上のサイトや高いトラフィックをさばかねば ならないサイトでは、可能なかぎりオーバヘッドを削るのは基本です。 そこでキャッシュが登場します。 コンテンツのキャッシュとは、コストのかかる計算で、かつ一度計算したら再度計 算する必要のないものの結果を保存することです。以下の疑似コードは、動的に生 成されるWeb ページで、キャッシュがどのように動作するかを説明しています:: ある URL に対し、キャッシュに該当するページがないか探す キャッシュ内にページがある場合: キャッシュされたページを返す それ以外: ページを生成する 生成されたページを (次のリクエスト用に) キャッシュに保存 生成されたページを返す。 Django には堅牢なキャッシュシステムが付属しており、動的なページを保存して、 リクエストの度に最計算しなくてもよいようになっています。利便性のため、 Django には様々な粒度でのキャッシュを提供しており。特定のビューだけをキャッ シュしたり、生成に時間を要する部分だけをキャッシュしたり、サイト全体をキャッ シュしたりできます。 Django は `Squid `_ のような「上流の」キャッシュ や、ブラウザベースのキャッシュともうまく協調できます。こうした類のキャッシュ は直接制御できませんが、サイトのどの部分をどのようにキャッシュすべきかを (HTTP ヘッダを介して) ヒントとして与えられます。 キャッシュを立ち上げる ====================== キャッシュシステムを使うには、少し設定が必要です。例えば、キャッシュデータ をどこに置くか、データベース上か、ファイルシステム上か、それともメモリ上か を指定せねばなりません。これはキャッシュのパフォーマンスに影響する重要な決 定です; そう、あるキャッシュ方式が別の方式より高速な場合もあるのです。 キャッシュの選択は設定ファイルの :setting:`CACHES` 設定で行います。 :setting:`CACHES` に設定できる値を以下に示します。 .. versionchanged:: 1.3 キャッシュの設定は Django 1.3 で変わりました。 Django 1.2 以前では :setting:`CACHE_BACKEND` という文字列でキャッシュの設定を行っていました。 この設定は :setting:`CACHES` という辞書に置き換えられました。 .. _memcached: Memcached --------- Django で利用できるキャッシュの中でも断然高速で、もっとも高効率である Memcached__ は、完全なメモリベースのキャッシュフレームワークです。 Memcached はもともと LiveJournal.com の高負荷を低減するために開発され、その後 Danga Interactive でオープンソース化されました。 Memcached は Slashdot や Wikipedia で使われており、データベースアクセスを低減して、サイトのパフォー マンスを劇的に向上させます。 __ http://memcached.org/ Memcached はデーモンとして動作し、指定された量の RAM の割り当てを受けます。 Memcached の役割は、キャッシュ内に任意のデータを追加し、取り出したり削除したりするた めのインタフェース、それも *超稲妻迅い* インタフェースを提供することにあり ます。全てのデータは直接メモリ上に保存されるので、データベースやファイルシ ステムの使用によるオーバヘッドがなくなります。 Memcached 本体のインストールの他に、 memcached の Python バインディングをイ ンストールする必要があります。 いくつかの Python バインディングがあります。 一般的なのは `python-memcached`_ と `pylibmc`_ です。 .. _`python-memcached`: ftp://ftp.tummy.com/pub/python-memcached/ .. _`pylibmc`: http://sendapatch.se/projects/pylibmc/ .. versionchanged:: 1.2 Django 1.0 と 1.1 では ``cmemcache`` バインディングも利用できましたが、 ``cmemcache`` がメンテナンスされていないため、 1.2 で 推奨されないライブラリ になりました。 ``cmemcache`` のサポートは Django 1.4 で無くなります。 .. versionchanged:: 1.3 ``pylibmc`` のサポートを追加しました。 Django と Memcached を組み合わせて使うには * :setting:`BACKEND ` に ``django.core.cache.backends.memcached.MemcachedCache`` か ``django.core.cache.backends.memcached.PyLibMCCache`` を 設定します(選んだ Memcached バインディングによります)。 * :setting:`LOCATION ` に ``ip:port`` を設定します。 ``ip`` は Memcached デーモンが動作している IP アドレスを、 ``port`` は、Memcached が動作しているポート番号を設定します。 あるいは、 ``unix:path`` を設定します。 ``path`` は Unix ソケットファイルのパスを設定します。 次の例は、 Memcached が localhost (127.0.0.1) の 11211 番ポートで動作していて、 ``python-memcached`` バインディングを利用している場合のものです。:: CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache', 'LOCATION': '127.0.0.1:11211', } } 次の例は、 Memcached が :file:`/tmp/memcached.sock` の Unix ソケットを通じて利用可能で、 ``python-memcached`` バインディングを利用している場合のものです。:: CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache', 'LOCATION': 'unix:/tmp/memcached.sock', } } memcached の素晴らしい点の一つは、複数のサーバ間でキャッシュを共有できると いうことです。この機能を利用するには、全てのサーバアドレスをセミコロンで区 切るか、リストで :setting:`LOCATION ` に設定します。 以下の例では、 IP アドレスが 172.19.26.240 と 172.19.26.242 で、いずれもポー ト番号 11211 で動作している memcached インスタンスでキャッシュを共有してい ます:: CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache', 'LOCATION': [ '172.19.26.240:11211', '172.19.26.242:11211', ] } } 以下の例では、 IP アドレスが 172.19.26.240 (ポート番号 11211) 、 172.19.26.242 (ポート番号 11212) 、 172.19.26.244 (ポート番号 11213) で 動作している Memcached インスタンスでキャッシュを共有しています。:: CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache', 'LOCATION': [ '172.19.26.240:11211', '172.19.26.242:11211', '172.19.26.244:11213', ] } } メモリベースのキャッシュには一つだけ短所があります: キャッシュデータはメモ リ上に保存されるので、サーバクラッシュ時に失われることがあります。いうまで もなく、メモリは永続的なデータ保存場所ではありません。ですから、データの保 存場所を確保するのにメモリベースのキャッシュに依存してはなりません。実際に は、 Django のキャッシュバックエンドは *どれも、永続的な記憶装置にはなりえません* -- キャッシュバックエンドは記憶 装置ではなく、あくまでもキャッシュ用です -- ただ、メモリベースのキャッシュ は特に一時性が高いため、ここで注意しておきます。 データベースを使ったキャッシュ ------------------------------ データベーステーブルをキャッシュバックエンドに使うには、まず以下のコマンド を実行してデータベース上にキャッシュテーブルを作成します:: python manage.py createcachetable [cache_table_name] ``[cache_table_name]`` は作成したいデータベーステーブルの名前です。 (この名 前は、現在データベースで既に使われていない有効なテーブル名なら何でも構いま せん。) このコマンドはデータベース中に Django のデータベースキャッシュシス テム向けの適切な形式のテーブルを生成します。 キャッシュ用のテーブルを作成したら、 :setting:`BACKEND ` 設定を ``"django.core.cache.backends.db.DatabaseCache"`` にし、 :setting:`LOCATION ` 設定を ``tablename`` にします。 ``tablename`` はキャッシュ用テーブルの名前です。 以下の例では、キャッシュテーブル名を ``my_cache_table`` にしています:: CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.db.DatabaseCache', 'LOCATION': 'my_cache_table', } } データベースを使ったキャッシュは、設定ファイルで指定しているのと同じデータベースを利用します。 キャッシュ用のテーブルを別のデータベースにすることはできません。 データベースのキャッシュがうまく働くのは、高速でインデクス構築のよくできた データベースサーバを使っている場合です。 データベースを使ったキャッシュと、マルチデータベース ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ データベースを使ったキャッシュをマルチデータベースで使っている場合には、 データベースキャッシュテーブルのルーティングを設定しなければなりません。 ルーティングのためのキャッシュテーブルは ``CacheEntry`` という名前のモデルとして、 アプリケーション名は ``django_cache`` という名前として扱われます。 このモデルはモデルキャッシュには現れませんが、ルーティングのために使えます。 例えば、このルーターはすべてのキャッシュの読み取り操作を ``cache_slave`` へ、 すべての書き込み操作を ``cache_master`` へルーティングします。 キャッシュテーブルは ``cache_master`` にだけ同期します。:: class CacheRouter(object): """すべてのデータベースキャッシュ操作をコントロールするルーター""" def db_for_read(self, model, **hints): "すべてのキャッシュ読み込み操作をスレーブへ" if model._meta.app_label in ('django_cache',): return 'cache_slave' return None def db_for_write(self, model, **hints): "すべてのキャッシュ読み込み操作をマスターへ" if model._meta.app_label in ('django_cache',): return 'cache_master' return None def allow_syncdb(self, db, model): "キャッシュモデルはマスターにだけ同期" if model._meta.app_label in ('django_cache',): return db == 'cache_master' return None データベースキャッシュモデルへルーティングの指示をしなかった場合には、キャッシュバックエンド は ``default`` データベースを利用します。 もちろん、データベースを使ったキャッシュを使わない場合には、データベースキャッシュモデルへの ルーティング指示について心配する必要はありません。 ファイルシステムを使ったキャッシュ ---------------------------------- ファイルシステム上にキャッシュしたい内容を置くには、 :setting:`BACKEND ` に ``"django.core.cache.backends.filebased.FileBasedCache"`` と指定します。 キャッシュデータを ``/var/tmp/django_cache`` に置きたいなら、以下の ように設定します:: CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache', 'LOCATION': '/var/tmp/django_cache', } } Windowsの場合には、ドライブレターを次の例のようにパスの先頭に置きます。:: CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache', 'LOCATION': 'c:/foo/bar', } } ディレクトリパスは常に絶対パス指定。すなわち、ファイルシステムのルートから 始まるパス名を指定せねばなりません。パスの末尾にスラッシュを追加するかどう かは問題にはなりません。 この設定の指し示すパスが実在し、 Web サーバを動かしているシステムユーザから 読み書き可能であるようにしてください。上の例でいうなら、サーバを ``apache`` というユーザで動かしている場合、 :file:`/var/tmp/django_cache` ディレクトリ が実在して、 ``apache`` によって読み書き可能かどうかをチェックしてください。 各キャッシュは別々のファイルに、 Python の ``pickle`` モジュールを使って シリアライズされた ("pickleされた") オブジェクト として保存されます。 各ファイル名はファイルシステムで利用できる安全な文字にエスケープされたキャッシュキーです。 ローカルメモリ上のキャッシュ ---------------------------- メモリを使ったキャッシュの恩恵を受けたい一方で、 memcached を動かせない状況 にある場合には、ローカルメモリを使ったキャッシュバックエンドを検討してみて ください。このキャッシュはマルチプロセスセーフかつスレッドセーフです。使う には、 :setting:`BACKEND ` に ``"django.core.cache.backends.locmem.LocMemCache"`` と指定します。 以下のようにして使います:: CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.locmem.LocMemCache', 'LOCATION': 'unique-snowflake' } } キャッシュの :setting:`LOCATION ` は個別のメモリストアを識別するために 使われます。ローカルメモリ上のキャッシュを一つだけ使う場合には、 :setting:`LOCATION ` は省略できます。ローカルメモリ上のキャッシュを複数 使う場合にはキャッシュを分けておくために少なくとも一つには名前を割り当てておく必要があります。 各サーバプロセスは、個別にキャッシュインスタンスを保持するので、プロセス間 でキャッシュは共有されません。このことから、明らかに、ローカルメモリキャッ シュのメモリ効率は非常に悪いといえます。おそらく実運用環境向きとではないで しょう。 ダミーキャッシュ (開発用) ------------------------- 最後に、 Django には「ダミーの」キャッシュが付いてきます。このキャッシュは 実際にはなにもしません -- 単に何もしないキャッシュインタフェースを実装して いるだけです。 ダミーキャッシュが便利になるのは、そこかしこで様々なキャッシュを使っている ような実運用サイトを構築していて、開発/テスト環境ではキャッシュを行いたく ないような場合です。開発環境ではダミーキャッシュによってキャッシュしなくな りますが、実運用環境はそのままにしておけます。ダミーキャッシュを有効にする には、次のように :setting:`BACKEND ` を設定します:: CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.dummy.DummyCache', } } カスタムのキャッシュバックエンドを使う ----------------------------------------- Django には、すぐに使えるキャッシュバックエンドがいくつもありますが、カスタ マイズしたバックエンドや自作のバックエンドを使いたい場合もあるでしょう。 外部のキャッシュバックエンドを Django と組み合わせたいなら、以下のように、 Python の import パスを :setting:`CACHE_BACKEND` に :setting:`BACKEND ` で指定します:: CACHES = { 'default': { 'BACKEND': 'path.to.backend', } } 自作のバックエンドを作成しているなら、リファレンス実装として標準のキャッシュ バックエンドを使うとよいでしょう。キャッシュバックエンドは Django のソース 中の :file:`django/core/cache/backends/` ディレクトリ下にあります。 注意: 運用ホスト上でサポートされていないなど、本当に特別な理由がないかぎり、 Django 組み込みのキャッシュバックエンドを使った方がよいでしょう。組み込みの バックエンドは良くテストされており、とても簡単に扱えます。 Cache の引数 -------------------- エンジンと名前のほかに、キャッシュの振る舞いをコントロールするためにキャッシュバックエンドは どれも引数をとれます。引数は :setting:`CACHES` のキーで指定します。 使える引数は以下の通りです: * :setting:`TIMEOUT `: デフォルトのタイムアウトで、単位は秒です。 デフォルト値は5 分 (300 秒) に設定されています。 * :setting:`OPTIONS `: キャッシュバックエンドへ渡したい オプションです。 使えるオプションは、バックエンドごとに様々です。 ``locmem`` や ``filesystem`` 、``database`` といったキャッシュバックエンドは それぞれ独自の淘汰方法を持っていて、次のオプションに従います。 * ``MAX_ENTRIES``: いくつまでキャッシュエントリを保持するかの設定です。 この設定を超えると古いものから削除されます。 デフォルト値は ``300`` です。 * ``CULL_FREQUENCY``: キャッシュエントリ数が ``MAX_ENTRIES`` に 達したときにどのくらいのキャッシュエントリを削除するかを分数で指定します。 実際の割合は ``1/CULL_FREQUENCY`` です。 つまり、``CULL_FREQUENCY`` を ``2`` に設定すると、 ``MAX_ENTRIES`` に達した場合に半分のキャッシュを削除します。 ``CULL_FREQUENCY`` に ``0`` を指定すると、キャッシュエントリ数が ``MAX_ENTRIES`` に到達した時に全てのキャッシュエントリを廃棄します。 の設定は、キャッシュミスの増加と引き換えに、淘汰処理を *劇的に* 高速化します。 サードパーティのライブラリを使ったキャッシュバックエンドはライブラリの オプションを背後のライブラリにじかにオプションを渡します。 結果として、有効なオプションのリストは使うライブラリに依存します。 * :setting:`KEY_PREFIX `: Djangoサーバが使うキャッシュキーに 自動的に付与される文字列です(デフォルトでは前につきます)。 頭につけられる文字列です。 詳細は :ref:`キャッシュのドキュメント ` を参照してください。 * :setting:`VERSION `: Djangoサーバが生成するキャッシュキーに使われる デフォルトのバージョン番号です。 詳細は :ref:`キャッシュのドキュメント ` を参照してください。 * :setting:`KEY_FUNCTION ` ドットで区切られた関数のパスを設定します。関数でキーの頭につけられる文字とバージョンを 最終的にどのように構成するかを定義します。 詳細は :ref:`キャッシュのドキュメント ` を参照してください。 ファイルシステムを使ったキャッシュを、デフォルトの タイムアウトが 60 秒で、 最大のキャッシュエントリ保持数が 1000 の設定です。:: CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache', 'LOCATION': '/var/tmp/django_cache', 'TIMEOUT': 60, 'OPTIONS': { 'MAX_ENTRIES': 1000 } } } 不正な引数や引数値は暗黙のうちに無視されます。 サイト単位のキャッシュ ====================== キャッシュを立ち上げたら、サイト全体をキャッシュするのが最も簡単な使い方で す。設定は設定ファイルの :setting:`MIDDLEWARE_CLASSES` に ``'django.middleware.cache.UpdateCacheMiddleware'`` と ``'django.middleware.cache.FetchFromCacheMiddleware'`` を追加するだけです。 例えば以下のようにします:: MIDDLEWARE_CLASSES = ( 'django.middleware.cache.UpdateCacheMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.cache.FetchFromCacheMiddleware', ) .. note:: ミドルウェアの順番は、これで間違っていません。 "Update" ミドルウェ アがリストの先頭で、 "Fetch" ミドルウェアが末尾です。詳しいからくりは ちょっとややこしいのですが、後述の `MIDDLEWARE_CLASSES の順番`_ で説明 しています。 次に、以下の必須の設定を Django 設定ファイルに追加します: * :setting:`CACHE_MIDDLEWARE_ALIAS` -- キャッシュを保存するストレージの エイリアスを指定します。 * :setting:`CACHE_MIDDLEWARE_SECONDS` -- 各ページのキャッシュ時間を秒単位で 指定します。 * :setting:`CACHE_MIDDLEWARE_KEY_PREFIX` -- 同じ Django の下にある複数のサ イト間でキャッシュを共有する場合、この値をサイトの名前にするか、 Django インスタンスごとに固有の文字列にして、キャッシュのキー衝突を防ぎます。キー 衝突を気にする必要がない場合は空文字列を設定します。 キャッシュミドルウェアはリスエストとレスポンスのヘッダで許可された場合、 GET と HEAD リクエストへのレスポンスをキャッシュします。 同じ URL で別のパラメータがついたリクエストは、ユニークなものとして認識し、 別々にキャッシュされます。 オプションとして :setting:`CACHE_MIDDLEWARE_ANONYMOUS_ONLY` を ``True`` に設定すると、アノニマスリクエスト(ログインしていないユーザのリクエスト)のみ がキャッシュされます。ユーザに特異なページのキャッシュを無効にする簡単で効果的な 設定です( Django の管理画面を含みます)。 :setting:`CACHE_MIDDLEWARE_ANONYMOUS_ONLY` の設定を使う場合には、 ``AuthenticationMiddleware`` を有効にするのを忘れないでください。 キャッシュミドルウェアは HEAD リクエストに対しての返答のレスポンスヘッダと GET リクエストへのレスポンスヘッダが同じであることを期待しています。 上記の場合 GET レスポンスのキャッシュを HEAD リクエストへ返します。 加えて、キャッシュミドルウェアは自動的に以下のヘッダを :class:`~django.http.HttpResponse` に追加します: * 「新鮮な」(キャッシュされていない) ページをリクエストされた場合には、 ``Last-Modified`` ヘッダを現在の date/time に設定します。 * ``Expires`` ヘッダを現在時刻と :setting:`CACHE_MIDDLEWARE_SECONDS` を加算した 値に設定します。 * :setting:`CACHE_MIDDLEWARE_SECONDS` に基づき、 ``Cache-Control`` ヘッダにページ の最長寿命を設定します。 ミドルウェアの詳細は :doc:`/topics/http/middleware` を参照してください。 ビューの中でキャッシュの有効期限を設定した場合 (``Cache-Control`` ヘッダの ``max-age`` セクションを設定した場合)、ページは :setting:`CACHE_MIDDLEWARE_SECONDS` の設定値ではなくビューで設定した有効期限の下で キャッシュされます。 ``django.views.decorators.cache`` モジュールのデコレー タを使えば、ビューの有効期限を設定 (``cache_control`` デコレータ) したり、 ビューのキャッシュを抑制 (``never_cache`` デコレータ) できます。これらのデ コレータについては `Vary ヘッダ以外のヘッダを使ったキャッシュ制御`__ を参照してください。 .. _i18n-cache-key: .. versionadded:: 1.2 :setting:`USE_I18N` を ``True`` に設定した場合、アクティブな :term:`language` 名付きのキャッシュキーが生成されます。 -- :ref:`how-django-discovers-language-preference` も参照してください。 マルチリンガルなサイトも自分でキーを生成すること無く容易にキャッシュできます。 .. versionchanged:: 1.4 アクティブな :term:`language ` は :setting:`USE_L10N` が ``True`` に設定されたときもキャッシュキーに含まれます。 また :setting:`USE_TZ` が ``True`` に設定された場合には :ref:`現在のタイムゾーン ` も含まれます。 __ `Vary ヘッダ以外のヘッダを使ったキャッシュ制御`_ ビュー単位のキャッシュ ====================== .. function:: django.views.decorators.cache.cache_page キャッシュフレームワークをもう少し低い粒度で使うには、個々のビューの出力を キャッシュします。 ``django.views.decorators.cache`` には関数デコレータ ``cache_page`` があり、自動的にビューからの応答をキャッシュします。 使い方は簡単です:: from django.views.decorators.cache import cache_page @cache_page(60 * 15) def my_view(request): ... ``cache_page`` は単一の引数をとります。これはキャッシュのタイムアウトを秒で 表したものです。上の例では、 ``my_view()`` の出力結果は 15 分間キャッシュ されます。(上の例では、可読性のために、 ``60 * 15`` と書いています。 ``60 * 15`` は ``900`` 、すなわち 60 秒を 15 回です) サイト単位のキャッシュのように、ビュー単位のキャッシュは URL 単位です。 複数の URL が同じビューに対してある場合には各URLごとに別々にキャッシュされます。 ``my_view`` を例にしましょう。 URLConf が以下のような場合には:: urlpatterns = ('', (r'^foo/(\d{1,2})/$', my_view), ) ``/foo/1/`` と ``/foo/23/`` へのリクエストは期待通り別々にキャッシュされます。 いったん固有の URL (たとえば ``/foo/23/`` )へのリクエストがあると、同じ URL への リクエストに対してはキャッシュが使われます。 ``cache_page`` はオプションのキーワード引数をとれます。 ``cache`` 引数はビューの結果をキャッシュする際に特定のキャッシュを指定できます( :setting:`CACHES` の設定のもの)。 デフォルトでは ``default`` キャッシュが使われますが、望みのキャッシュを指定できます:: @cache_page(60 * 15, cache="special_cache") def my_view(request): ... ビュー単位でキャッシュのプリフィックスの設定を上書きすることもできます。 ``cache_page`` は ``key_prefix`` というキーワード引数をとれます。 ``key_prefix`` はミドルウェアに対する :setting:`CACHE_MIDDLEWARE_KEY_PREFIX` と同様に機能します。次のように使います:: @cache_page(60 * 15, key_prefix="site1") def my_view(request): ... 二つの設定は組み合わせて使用できます。 ``cache`` *と* ``key_prefix`` を 指定すると、設定されているキャッシュエイリアスのキャッシュのプリフィックスを 上書きしたものを取得できます。 URLconf でビュー単位のキャッシュを指定する -------------------------------------------- ここまでのセクションに登場した例は ``my_view`` 関数自身を ``cache_page`` で 置き換えていたので、ビューがキャッシュされるようにハードコードされていました。 この方法はビューとキャッシュシステムを密結合してしまうため、いくつかの理由において 理想的ではありません。 実際は、ビュー関数をほかのキャッシュしないサイトで再利用したくなるかもしれませんし、 キャッシュを使わないかもしれない人々にビュー関数を配布したいかもしれません。 このような問題を解決するには、ビュー関数自身ではなく、 URLconf でビュー単位の キャッシュを設定します。 URLconf への設定は簡単にできます: URLconf でビュー関数を ``cache_page`` で ラップするだけです。もともとの URLconf がこうなっているとします:: urlpatterns = ('', (r'^foo/(\d{1,2})/$', my_view), ) ``cache_page`` で ``my_view`` をラップするとこうなります。:: from django.views.decorators.cache import cache_page urlpatterns = ('', (r'^foo/(\d{1,2})/$', cache_page(60 * 15)(my_view)), ) .. templatetag:: cache テンプレートの部分的キャッシュ ================================ ページのキャッシュをもっと細かく制御したいなら、 ``cache`` テンプレートタグ を使って、テンプレートの一部分だけをキャッシュできます。 ``cache`` タグをテ ンプレート内で使えるようにするには、テンプレートの冒頭に ``{% load cache %}`` を挿入しておきます。 ``{% cache %}`` テンプレートタグは、タグで囲まれたブロックの内容を、指定さ れた時間だけキャッシュします。 ``cahce`` タグは、必須の引数として、キャッシュ の有効期限 (秒単位) とキャッシュされた部分に付ける名前をとります。例えば、 以下のように使います: .. code-block:: html+django {% load cache %} {% cache 500 sidebar %} .. sidebar .. {% endcache %} キャッシュ部分内で使われる変数に応じて、複数のキャッシュコピーを保存したい 場合もあるでしょう。例えば、上の例で挙げたサイドバー部分を、ユーザ毎に個別 にキャッシュしたい場合には、 ``{% cache %}`` テンプレートタグに追加の引数を 指定して、キャッシュをユーザ毎に一意に識別できるようにします: .. code-block:: html+django {% load cache %} {% cache 500 sidebar request.user.username %} .. sidebar for logged in user .. {% endcache %} キャッシュ変数は、複数指定しても全く問題ありません。必要なだけの引数を ``{% cache %}`` に指定してください。 :setting:`USE_I18N` が ``True`` にセットされている場合、 サイト単位のミドルウェアキャッシュは :ref:`アクティブな言語を優先します ` 。 ``cache`` テンプレート内で同じ結果を取得するために、テンプレートタグは :ref:`翻訳用の変数 ` を使えます。 .. code-block:: html+django {% load i18n %} {% load cache %} {% get_current_language as LANGUAGE_CODE %} {% cache 600 welcome LANGUAGE_CODE %} {% trans "Welcome to example.com" %} {% endcache %} キャッシュのタイムアウトはテンプレート変数でも指定できます。ただし、テンプ レート変数は整数値でなければなりません。例えば、テンプレート変数 ``my_timeout`` を ``600`` にセットしていれば、以下の二つの例は同じ効果をも たらします: .. code-block:: html+django {% cache 600 sidebar %} ... {% endcache %} {% cache my_timeout sidebar %} ... {% endcache %} この機能を使えば、テンプレートで何度もタイムアウトを編集しなくても、一箇所 で変数を宣言しておいて再利用できるので便利です。 低水準のキャッシュ API ====================== .. highlight:: python ページ全体のキャッシュがあまり有効でなく、ともするとやりすぎでかえって不便 な場合があります。 恐らく実際には、いくつかのコストの高い、結果の更新頻度が違うクエリーを含んでいるでしょう。 こういう場合には、サイト単位や、ビュー単位のキャッシュが提供するページ全体のキャッシュは 理想的ではありません。(いくつかのデータは頻繁に更新されるので)全部の結果はキャッシュしたく なくて、それでも変更が少ない結果はキャッシュしたいでしょうから。 こういう場合のために、 Django は低レベルなキャッシュ API を公開しています。 この API は好きな粒度でオブジェクトをキャッシュに格納するのに使えます。 安全に pickle できる Python オブジェクトであれば、文字列でも辞書でも、モデルオブジェクトの リストでも、なんでもキャッシュできます(たいていの Python オブジェクトは pickle できます。 pickle に関して、より詳しくは Python のドキュメントを参照してください)。 キャッシュを表現するモジュールである ``django.core.cache`` は :setting:`CACHES` の ``'default'`` エントリ設定に基づいて生成された ``cache`` オブジェクトを公開しています:: >>> from django.core.cache import cache 基本となるインタフェースは ``set(key, value, timeout)`` と ``get(key)`` です:: >>> cache.set('my_key', 'hello, world!', 30) >>> cache.get('my_key') 'hello, world!' ``timeout`` 引数はオプションで、デフォルト値は :setting:`CACHES` の ``default`` バックエンド設定の timeout 引数の値になります (これについては上記を参照してください)。 キャッシュに保存する秒数です。 オブジェクトがキャッシュの中になければ、 ``cache.get()`` は ``None`` を返し ます:: # Wait 30 seconds for 'my_key' to expire... >>> cache.get('my_key') None リテラルで ``None`` をキャッシュにしまうことは賛成できません。 なぜなら ``None`` が保持されていたのか、キャッシュに見つからなくて ``None`` が 返されたかの区別ができないでしょうから。 ``cache.get()`` には ``default`` 引数を指定できます。 ``default`` には、オ ブジェクトがキャッシュの中にないときに返す値を指定します:: >>> cache.get('my_key', 'has_expired') 'has_expired' キーに使う値がまだキャッシュ辞書上に存在しない場合にのみ、 ``add()`` メソッ ドを使ってください。 ``add()`` メソッドは、 ``set()`` と同じ引数をとります が、指定したキーがすでに存在する場合には、キャッシュを更新しません:: >>> cache.set('add_key', 'Initial value') >>> cache.add('add_key', 'New value') >>> cache.get('add_key') 'Initial value' ``add()`` によってキャッシュにデータが保存されたどうかを知りたければ、戻り 値をチェックしてください。戻り値が ``True`` なら、保存されています。そうで ないときは ``False`` を返します。 キャッシュを一度しかアクセスしない ``get_many()`` インタフェースもあります。 ``get_many()`` は指定した全てのキーのうち、キャッシュ内に実在する (そして期 限切れでない) ものの入った辞書を返します:: >>> cache.set('a', 1) >>> cache.set('b', 2) >>> cache.set('c', 3) >>> cache.get_many(['a', 'b', 'c']) {'a': 1, 'b': 2, 'c': 3} .. versionadded:: 1.2 複数の値を効率的にセットするには、 ``set_many()`` にキーと値の辞書を渡します:: >>> cache.set_many({'a': 1, 'b': 2, 'c': 3}) >>> cache.get_many(['a', 'b', 'c']) {'a': 1, 'b': 2, 'c': 3} ``cache.set()`` のように、 ``set_many()`` はオプションで ``timeout`` 引数をとります。 キーは ``delete()`` で明示的に削除できます。特定のオブジェクトのキャッシュをクリアする 簡単な方法です。 >>> cache.delete('a') .. versionadded:: 1.2 一度にたくさんのキーをクリアしたい場合には、 ``delete_meny()`` にクリアしたいキーの リストを渡します。 >>> cache.delete_many(['a', 'b', 'c']) .. versionadded:: 1.2 最後に、すべてのキーをキャッシュから削除したい場合には、 ``cache.clear()`` を 使います。注意が必要なのは、 ``clear()`` はキャッシュから **すべてを** 削除することです。 あなたのアプリケーションがセットしたキーにとどまりません。 >>> cache.clear() 既に登録済みのキーを増減したい場合には、それぞれ ``incr()`` 、 ``decr()`` メソッド を使います。デフォルトではキャッシュの値は 1 ずつ増減されます。増減の値は引数で与えられます。 もし、存在しないキーに対して増減しようとした場合には ValueError を送出します:: >>> cache.set('num', 1) >>> cache.incr('num') 2 >>> cache.incr('num', 10) 12 >>> cache.decr('num') 11 >>> cache.decr('num', 5) 6 .. note:: ``incr()``/``decr()`` メソッドはアトミックが保証されません。 アトミックな増減をサポートしているバックエンド( memcached バックエンドとか)は 増減の操作はアトミックになります。 一方、増減の操作をネイティブに提供していないバックエンドは取得と更新という2ステップで 実装されるでしょう。 .. _cache_key_prefixing: キャッシュキーのプリフィックス ------------------------------- .. versionadded:: 1.3 複数のサーバ間でキャッシュインスタンスを共有している場合や本番環境と 開発環境で共有している場合には、あるサーバのキャッシュデータを他の サーバに使われてしまうことがあります。 キャッシュデータのフォーマットがサーバ間でつがう場合には突き止めるのが 非常に難しい問題を引き起こしがちです。 この問題を避けるために、すべてのキャッシュキーにプリフィックスをつけられます。 個別のキャッシュキーを保存するときや取得するときに キャッシュの :setting:`KEY_PREFIX ` に設定された 値を Django が自動でプリフィックスをつけます。 各 Django インスタンスの :setting:`KEY_PREFIX ` を確実に別のものに設定しておくことで、キャッシュが衝突することを避けられます。 .. _cache_versioning: キャッシュバージョン -------------------- .. versionadded:: 1.3 キャッシュの値を利用するコードを変更した場合、既存のキャッシュを きれいにする必要があるかもしれません。 一番簡単な方法は前キャッシュをフラッシュすることですが、まだ使える 有効なキャッシュをロスしてしまいます。 Django は個別のキャッシュをターゲットによりより手段を用意しています。 Django のキャッシュフレームワークは :setting:`VERSION ` で設定できるシステムワイドなバージョン識別子を持っています。 この設定の値はユーザが指定したキーとキャッシュプリフィックスと自動的に 組み合わされて最終的なキャッシュキーが生成されます。 デフォルトでは、すべてのキーリクエストは自動的にサイトのデフォルト キャッシュキーバージョンが含まれます。 しかし、基本的なキャッシュ関数は、 set と get でキャッシュキーバージョンを 指定できる世に ``version`` 引数をとれます。:: # Set version 2 of a cache key >>> cache.set('my_key', 'hello world!', version=2) # Get the default version (assuming version=1) >>> cache.get('my_key') None # Get version 2 of the same key >>> cache.get('my_key', version=2) 'hello world!' 特定キーのバージョンは :func:`incr_version()` と :func:`decr_version()` メソッドを 使うことで増減できます。 他のキーには影響を与えずに、特定キーを新しいバージョンに押し出せます。前の例に続けると:: # Increment the version of 'my_key' >>> cache.incr_version('my_key') # The default version still isn't available >>> cache.get('my_key') None # Version 2 isn't available, either >>> cache.get('my_key', version=2) None # But version 3 *is* available >>> cache.get('my_key', version=3) 'hello world!' .. _cache_key_transformation: キャッシュキーの変換 ------------------------ .. versionadded:: 1.3 ここまでの2つのセクションで解説した通り、ユーザの与えたキャッシュキーは そのままでは利用されません。キャッシュプリフィックスとキャッシュキー バージョンと組み合わせて、最終的なキャッシュキーが与えられます。 デフォルトでは3つの部分がコロンで区切られた文字列が与えられます。 def make_key(key, key_prefix, version): return ':'.join([key_prefix, str(version), smart_str(key)]) 別の方法で組み合わせたり、(キーの部分を八種ダイジェストしたりと)他の 処理をキーに加えたい場合には、カスタムキー関数を定義できます。 キャッシュの設定で :setting:`KEY_FUNCTION ` に 上で紹介した :func:`make_key()` と引数を あわせた関数へのパスをドットで区切られたパスで指定します。 設定がされていれば、デフォルトのキー組み合わせ関数の代わりに利用されます。 キャッシュキーに関する注意 -------------------------- .. versionadded:: 1.3 Memcached は一番一般的に本番環境のキャッシュバックエンドで使われていますが、 250文字以上のキャッシュキーや、空白・制御文字を含めたキーは許されません(使用 すると例外が発生します)。 キャッシュのコードをポータブルにするために、また好ましくないサプライズを最小限 にするために、他のビルトインキャッシュバックエンドは、 memcached で利用すると エラーになるキーが使われた場合には、ウォーニングを出します ( ``django.core.cache.backends.base.CacheKeyWarning`` )。 いろいろなキーを受け付けるキャッシュバックエンド(カスタムバックエンドや memcached 以外のビルトインバックエンド)を使っていて、ウォーニングなしでいろいろなキーを利用したい 場合には、 ``CacheKeyWarning`` をおとなしくさせられます。 :setting:`INSTALLED_APPS` に設定しているうちの一つの ``management`` モジュール に、次のコードを追加します。:: import warnings from django.core.cache import CacheKeyWarning warnings.simplefilter("ignore", CacheKeyWarning) ビルトインバックエンドでキーのバリデーションロジックを交換したい場合には、 バックエンドを継承して ``validate_key`` メソッドを上書きして、 `カスタムのキャッシュバックエンドを使う`_ に従うだけです。 実際に ``locmem`` バックエンドで行う場合には、このコードをモジュールに 追加します。:: from django.core.cache.backends.locmem import LocMemCache class CustomLocMemCache(LocMemCache): def validate_key(self, key): """Custom validation, raising exceptions or warnings as needed.""" # ... ...ドットで区切られたこのクラスの Python のパスを :setting:`CACHES` 設定の :setting:`BACKEND ` 部分に設定します。 上流キャッシュ ============== ここまでは、 *自分の* データに対するキャッシュについて説明してきました。し かし、 Web 開発にはもう一つのタイプのキャッシュが関係してきます。それ は 「上流 (upstream)」のキャッシュ機構で行われているキャッシュです。上流の キャッシュは、ユーザのリクエストが Web サイトに到達する前ですらページのキャッ シュを行います。 上流キャッシュの例をいくつか示します: * ISP がある程度のページをキャッシュしていることもありますので、 http://example.com/ へリクエストしたページを ISP は example.com へ アクセスせずに返してくることがあります。 example.com のメンテナは、このキャッシュについて知りません。 ISP は example.com とブラウザの間にいて、すべてのキャッシュハンドリングを 透過的に行います。 * ページをキャッシュしてパフォーマンスを向上させるために、 Django Web サイトを Squid Web プロキシ (http://www.squid-cache.org/) の背後に置 けます。この場合、リクエストはまず Squid でさばかれ、必要な時にのみア プリケーションに渡されるようになります。 * Web ブラウザもページをキャッシュします。 Web ページが適切なヘッダを送 信すると、ブラウザは以後の同じページへのリクエストにはローカルの (キャッ シュされた) コピーを使うようになります。 上流のキャッシュは効率を高める良い方法ではありますが、危険もはらんでいます: 多くの Web ページのコンテンツは認証に応じて異なる内容になります。また、その 他の変数も入ります。キャッシュシステムが純粋に URL だけに基づいてページを 盲目的に保存してしまうと、同じページを後から見た訪問者に対して正しくない情 報や機密の情報を晒してしまいます。 例えば、 Web ベースの email システムを操作しているとしましょう。"inbox" ページのコンテンツはいうまでもなくログインしているユーザ固有のものです。 ある ISP が盲目的にサイトをキャッシュしてしまうと、その ISP を経由して最初 にログインしたユーザは自分の inbox ページをキャッシュしてしまい、以降に そのサイトを訪れたユーザが閲覧できるようになってしまいます。これはよろしく ありません。 幸運にも、 HTTP にはこうした問題に対する解決策があります。すなわち、キャッ シュ機構に指定の変数に基づいてコンテンツのキャッシュを行うよう指示したり、 キャッシュメカニズムが特定のページをキャッシュしないように指示したりする 一連の HTTP ヘッダがあるのです。 .. _using-vary-headers: Vary ヘッダを使う ================= ``Vary`` ヘッダは、キャッシュ機構がキャッシュキーを生成するときに、どのリク エストヘッダを考慮すべきかを定義しています。例えば、 Web ページのコンテンツ が言語設定に依存している場合、ページは「言語によって変化 (vary)」します。 .. versionchanged:: 1.3 Django 1.3 でクエリを含むフルリクエストパスがキャッシュキーの 生成に使われるようになりました。 Django 1.2 ではパス部分のみでした。 デフォルトでは、 Django のキャッシュシステムはキャッシュキーをリクエストの パス部分とクエリ、例えば ``/stories/2005/?order_by=author"`` を使って生成します。 この場合、クッキーや言語設定のようなユーザエージェント間の違いにかかわらず、 同じ URL を指すリクエストは全て同じバージョンのキャッシュを使います。 しかし、クッキーや言語、ユーザエージェントといったリクエストヘッダ上の違い に基づいて、違った内容を出力する場合、 ``Vary`` ヘッダを使って、ページ出力 が何に依存しているかをキャッシュメカニズムに教える必要があります。 Django で ``Vary`` ヘッダを設定するには、以下のような便宜用のビュー関数デコ レータ、 ``vary_on_headers`` を使います:: from django.views.decorators.vary import vary_on_headers @vary_on_headers('User-Agent') def my_view(request): ... 上の場合では、 (Django 自体のキャッシュミドルウェアのような) キャッシュメカ ニズムは個々のユーザエージェント固有の別のバージョンをキャッシュします。 ``Vary`` ヘッダを (``response['Vary'] = 'user-agent'`` のような操作で) 手動 で変更せずに、 ``vary_on_headers`` デコレータを使う利点は、デコレータが (す でに存在するかもしれない) ``Vary`` ヘッダをスクラッチから作るのではなく、き ちんと追加処理を行う点にあります。 ``vary_on_headers()`` には複数のヘッダを渡せます:: @vary_on_headers('User-Agent', 'Cookie') def my_view(request): # ... これは上流キャッシュに *両方* の変化を伝えます。つまり、ユーザエージェントとクッキー の組み合わせでキャッシュを取得します。例えば、ユーザエージェントが ``Mozilla`` で クッキーの値が ``foo=bar`` は、ユーザエージェントが ``Mozilla`` で、 クッキーの値が ``foo=ham`` のリクエストと別のものとして考慮されます。 クッキーによるコンテンツの変更はよくあることなので、 ``vary_on_cookie`` デコレータも用意されています。従って、以下の二つのビューは同じ振舞いをします:: @vary_on_cookie def my_view(request): ... @vary_on_headers('Cookie') def my_view(request): ... ``vary_on_headers`` に渡すヘッダは大小文字を区別しないので注意してください。 ``"User-Agent"`` は ``"user-agent"`` と同じです。 ヘルパ関数 ``django.utils.cache.patch_vary_headers()`` も直接使えます:: from django.utils.cache import patch_vary_headers def my_view(request): # ... response = render_to_response('template_name', context) patch_vary_headers(response, ['Cookie']) return response ``patch_vary_headers`` は第一引数に :class:`~django.http.HttpResponse` インスタンスをとり、ヘッ ダ名のリストまたはタプルを第二引数にとります。 Vary ヘッダの詳細は `公式の Vary の仕様`_ を参照してください。 .. _`公式の Vary の仕様`: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.44 Vary ヘッダ以外のヘッダを使ったキャッシュ制御 ============================================= キャッシュの利用で起きるもう一つの問題は、データのプライバシーと、カスケー ド接続したキャッシュのどこにデータを保存すべきかという疑問です。 通常、ユーザの目に触れるのは二種類のキャッシュ、すなわち自分のブラウザのキャッ シュ (プライベートのキャッシュ) と、ページプロバイダ側のキャッシュ (公開の キャッシュ) です。公開のキャッシュは複数のユーザによって利用されており、別 のユーザがその内容を制御することもあります。これは、注意の必要なデータを扱 う際には問題になります: 例えば、銀行のアカウント番号を公開キャッシュに保存 して欲しくはないでしょう。つまり、 Web アプリケーションにはどのデータがプラ イベートで、どのデータが公開なのかを区別する方法が必要なのです。 この問題の解決策は、ページキャッシュが「プライベート」であると示すことです。 Django では、 ``cache_control`` ビューデコレータを使ってこれを実現します。 例えば:: from django.views.decorators.cache import cache_control @cache_control(private=True) def my_view(request): # ... このデコレータは、適切な HTTP ヘッダが送信されるように背後で気を配ります。 「プライベート」と「パブリック」キャッシュ制御は排他的であることを覚えておいてください。 デコレータは「プライベート」がセットされるべきである場合には「パブリック」ディレクティブ を取り除きます(逆も同じです)。プライベートとパブリックなエントリの両方を持つブログサイトで 二つのディレクティブを使う例です。パブリックなエントリは共有キャッシュとしてキャッシュされます。 コードに登場する ``patch_cache_controll`` はキャッシュ制御ヘッダをマニュアルで修正 します(内部的には ``cache_control`` デコレータを呼び出します)。:: from django.views.decorators.cache import patch_cache_control from django.views.decorators.vary import vary_on_cookie @vary_on_cookie def list_blog_entries_view(request): if request.user.is_anonymous(): response = render_only_public_entries() patch_cache_control(response, public=True) else: response = render_private_and_public_entries(request.user) patch_cache_control(response, private=True) return response 他にもキャッシュパラメタを操作する方法がいくつかあります。例えば、 HTTP を 使うアプリケーションは以下のような操作を行えます: * ページの最大キャッシュ回数を定義できます。 * キャッシュされているコンテンツの新たなバージョンがないか常に調べ、変 更がないときに限ってキャッシュを送信するように設定できます (キャッシュ によっては、サーバ上のページが変更されていても、単にキャッシュコピー の有効期限が切れていないという理由でキャッシュされた内容を配信するこ とがあります)。 Django では、ビュー関数デコレータの ``cache_control`` を使って、キャッシュ パラメタを設定します。以下の例では、 ``cache_control`` を使って、アクセス ごとにコンテンツの再検証を行い、キャッシュされたバージョンの最大保存期限を 3600 秒に設定しています:: from django.views.decorators.cache import cache_control @cache_control(must_revalidate=True, max_age=3600) def my_view(request): # ... 有効な ``Cache-Control`` HTTP ディレクティブは全て ``cache_control()`` に 使えます。利用できるディレクティブを示します: * ``public=True`` * ``private=True`` * ``no_cache=True`` * ``no_transform=True`` * ``must_revalidate=True`` * ``proxy_revalidate=True`` * ``max_age=num_seconds`` * ``s_maxage=num_seconds`` Cache-Control HTTP ディレクティブの説明は `Cache-Control の仕様`_ を参照し てください。 (キャッシュミドルウェアは常にキャッシュヘッダの最長寿命 (max-age) を ``CACHE_MIDDLEWARE_SETTINGS`` の設定値に設定するので注意してください。カス タムの ``max_age`` を ``cache_control`` デコレータで使うと、デコレータの設 定が優先され、ヘッダの値は正しくマージされます。) ヘッダを使ってキャッシュを抑制したい場合には、 ``django.views.decorators.cache.never_cache`` を使ってください。このデコレー タは、応答コンテンツがブラウザやその他のキャッシュ機構によってキャッシュさ れないようにヘッダを追加します:: from django.views.decorators.cache import never_cache @never_cache def myview(request): # ... .. _`Cache-Control の仕様`: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9 その他の最適化 ============== Django には、アプリケーションのパフォーマンスを最適化する上で役立つミドルウェ アが他にもいくつかあります: * ``django.middleware.http.ConditionalGetMiddleware`` を使うと、条件付 き GET をサポートできるようになり、 ``ETag`` および ``Last-Modified`` ヘッダを使えるようになります。 * ``django.middleware.gzip.GZipMiddleware`` は gzip 圧縮を扱えるブラウ ザ (最近のほとんどのブラウザが対応しています) に対して、コンテンツを gzip で圧縮します。 MIDDLEWARE_CLASSES の順番 ========================= キャッシュ関連のミドルウェアを使う場合、 ``MIDDLEWARE_CLASSES`` 設定中の 正しい場所に配置することが重要です。というのも、キャッシュミドルウェアは キャッシュストレージ上のコンテンツとの差異を検出するために、どのヘッダが変 更されたかを調べる必要があるからです。ミドルウェアは通常、必要に応じて ``Vary`` レスポンスヘッダに情報を付加します。 ``UpdateCacheMiddleware`` はレスポンスフェイズに動作します。レスポンスフェ イズのミドルウェアは逆順に処理されるので、リストの先頭のミドルウェアは *最後* に呼び出されます。従って、 ``UpdateCacheMiddleware`` は、 ``Vary`` ヘッダに何らかの情報を付加するミドルウェアよりも *手前* に追加せねばなりま せん。以下のミドルウェアが、 ``Vary`` ヘッダを操作します: * ``SessionMiddleware`` は ``Cookie`` を追加します。 * ``GZipMiddleware`` は ``Accept-Encoding`` を追加します。 * ``LocaleMiddleware`` は ``Accept-Language`` を追加します。 一方、 ``FetchFromCacheMiddleware`` はリクエストフェイズに動作します。リク エストフェイズでは、ミドルウェアは先頭から末尾に向けて処理されるので、 リストの先頭にあるミドルウェアが *最初* に呼び出されます。 ``FetchFromCacheMiddleware`` もまた、 ``Vary`` ヘッダを操作するミドルウェア よりも後に呼び出さねばならないので、 ``FetchFromCacheMiddleware`` は *後ろ* に置かねばなりません。