条件付きビュー処理—Djangoドキュメント

提供:Dev Guides
< DjangoDjango/docs/3.2.x/topics/conditional-view-processing
移動先:案内検索

条件付きビュー処理

HTTPクライアントは、すでに見たリソースのコピーについてサーバーに通知するために、いくつかのヘッダーを送信できます。 これは、クライアントがすでに取得したもののすべてのデータを送信しないようにするために、(HTTP GET要求を使用して)Webページを取得するときに一般的に使用されます。 ただし、すべてのHTTPメソッド(POSTPUTDELETEなど)に同じヘッダーを使用できます。

Djangoがビューから送り返すページ(応答)ごとに、ETagヘッダーとLast-Modifiedヘッダーの2つのHTTPヘッダーが提供される場合があります。 これらのヘッダーは、HTTP応答ではオプションです。 これらは、ビュー関数で設定することも、 ConditionalGetMiddleware ミドルウェアを使用してETagヘッダーを設定することもできます。

クライアントが次に同じリソースを要求すると、 If-modified-since または If-unmodified-sinceなどのヘッダーを送信する場合があります。 、送信された最終変更時刻の日付、または If-match または If-none-match のいずれかが含まれます]、最後に送信されたETagを含みます。 ページの現在のバージョンがクライアントから送信されたETagと一致する場合、またはリソースが変更されていない場合は、完全な応答の代わりに304ステータスコードを返送して、クライアントに何も持っていないことを通知できます。かわった。 ヘッダーによっては、ページが変更されているか、クライアントから送信されたETagと一致しない場合、412ステータスコード(前提条件が失敗しました)が返される場合があります。

よりきめ細かい制御が必要な場合は、ビューごとの条件付き処理関数を使用できます。

conditionデコレータ

場合によっては(実際、かなり頻繁に)、 ETag 値またはリソースの最終変更時刻を迅速に計算する関数を作成できますが、を実行する必要はありません。フルビューを構築するために必要なすべての計算。 Djangoはこれらの関数を使用して、ビュー処理の「早期ベイルアウト」オプションを提供できます。 おそらく、最後のリクエスト以降、コンテンツが変更されていないことをクライアントに通知します。

これらの2つの関数は、パラメーターとしてdjango.views.decorators.http.conditionデコレーターに渡されます。 このデコレータは、2つの関数(両方の量を簡単かつ迅速に計算できない場合は、1つだけ指定する必要があります)を使用して、HTTPリクエストのヘッダーがリソースのヘッダーと一致するかどうかを判断します。 それらが一致しない場合は、リソースの新しいコピーを計算する必要があり、通常のビューが呼び出されます。

conditionデコレータの署名は次のようになります。

condition(etag_func=None, last_modified_func=None)

ETagと最終変更時刻を計算する2つの関数には、受信requestオブジェクトと同じパラメーターが、ラップに役立つビュー関数と同じ順序で渡されます。 last_modified_funcに渡された関数は、リソースが最後に変更された時刻を指定する標準の日時値を返すか、リソースが存在しない場合はNoneを返す必要があります。 etagデコレータに渡される関数は、リソースの ETag を表す文字列を返すか、存在しない場合はNoneを返す必要があります。

デコレータは、ETagおよびLast-Modifiedヘッダーがビューによってまだ設定されておらず、リクエストのメソッドが安全である場合(GETまたはHEAD)、応答に設定します。 ])。

この機能を有効に使用することは、おそらく例で最もよく説明されています。 小さなブログシステムを表す、このモデルのペアがあるとします。

import datetime
from django.db import models

class Blog(models.Model):
    ...

class Entry(models.Model):
    blog = models.ForeignKey(Blog, on_delete=models.CASCADE)
    published = models.DateTimeField(default=datetime.datetime.now)
    ...

最新のブログエントリを表示するフロントページが、新しいブログエントリを追加したときにのみ変更される場合は、最終変更時刻を非常にすばやく計算できます。 そのブログに関連付けられているすべてのエントリには、最新のpublishedの日付が必要です。 これを行う1つの方法は次のとおりです。

def latest_entry(request, blog_id):
    return Entry.objects.filter(blog=blog_id).latest("published").published

次に、この機能を使用して、フロントページビューの変更されていないページを早期に検出できます。

from django.views.decorators.http import condition

@condition(last_modified_func=latest_entry)
def front_page(request, blog_id):
    ...

デコレータの順序に注意してください

condition()が条件付き応答を返す場合、その下のデコレータはスキップされ、応答には適用されません。 したがって、通常のビュー応答と条件付き応答の両方に適用する必要のあるデコレータは、condition()より上にある必要があります。 特に、 vary_on_cookie()vary_on_headers()、および cache_control()は、 RFC 7232 [X146X ]は、それらが設定するヘッダーが304応答に存在することを要求します。


1つの値のみを計算するためのショートカット

原則として、ETagと最終変更時刻の両方を計算する関数を提供できる場合は、そうする必要があります。 特定のHTTPクライアントがどのヘッダーを送信するかわからないため、両方を処理する準備をしてください。 ただし、計算が簡単な値は1つだけで、DjangoはETagのみまたは最後に変更された計算のみを処理するデコレータを提供する場合があります。

django.views.decorators.http.etagおよびdjango.views.decorators.http.last_modifiedデコレータには、conditionデコレータと同じタイプの関数が渡されます。 それらの署名は次のとおりです。

etag(etag_func)
last_modified(last_modified_func)

これらのデコレータの1つを使用して、最後に変更された関数のみを使用する前の例を書くことができます。

@last_modified(latest_entry)
def front_page(request, blog_id):
    ...

…また:

def front_page(request, blog_id):
    ...
front_page = last_modified(latest_entry)(front_page)

両方の条件をテストするときは、conditionを使用してください

両方の前提条件をテストしたい場合は、etagデコレータとlast_modifiedデコレータをチェーンしてみることをお勧めします。 ただし、これは誤った動作につながります。

# Bad code. Don't do this!
@etag(etag_func)
@last_modified(last_modified_func)
def my_view(request):
    # ...

# End of bad code.

最初のデコレータは2番目のデコレータについて何も知らず、2番目のデコレータが別の方法で決定したとしても応答は変更されないと答える場合があります。 conditionデコレータは、両方のコールバック関数を同時に使用して、実行する適切なアクションを実行します。


他のHTTPメソッドでデコレータを使用する

conditionデコレータは、GETおよびHEADリクエスト以外にも役立ちます(この状況では、HEADリクエストはGETと同じです) 。 また、POSTPUT、およびDELETE要求のチェックを提供するために使用することもできます。 このような状況では、「変更されていない」応答を返すのではなく、変更しようとしているリソースがその間に変更されたことをクライアントに通知するという考え方です。

たとえば、クライアントとサーバー間の次の交換について考えてみます。

  1. クライアントは/foo/を要求します。
  2. サーバーは、"abcd1234"のETagを持つコンテンツで応答します。
  3. クライアントは、HTTP PUT要求を/foo/に送信して、リソースを更新します。 また、If-Match: "abcd1234"ヘッダーを送信して、更新しようとしているバージョンを指定します。
  4. サーバーは、GETリクエストの場合と同じ方法で(同じ関数を使用して)ETagを計算することにより、リソースが変更されたかどうかを確認します。 リソース変更された場合、「前提条件が失敗しました」を意味する412ステータスコードが返されます。
  5. クライアントは、412応答を受信した後、GET要求を/foo/に送信して、コンテンツを更新する前に、コンテンツの更新バージョンを取得します。

この例が示す重要なことは、すべての状況で同じ関数を使用してETagと最終変更値を計算できることです。 実際、は同じ関数を使用する必要があるので、毎回同じ値が返されます。

安全でないリクエストメソッドを含むバリデーターヘッダー

conditionデコレータは、安全なHTTPメソッドのバリデーターヘッダー(ETagおよびLast-Modified)のみを設定します。 GETおよびHEAD。 それ以外の場合に返品したい場合は、ビューに設定してください。 PUTPOSTで行われた要求に応答してバリデーターヘッダーを設定することの違いについては、 RFC 7231#section-4.3.4 を参照してください。 。


ミドルウェアの条件付き処理との比較

Djangoは、 django.middleware.http.ConditionalGetMiddleware を介して条件付きGET処理を提供します。 ミドルウェアは多くの状況に適していますが、高度な使用には制限があります。

  • プロジェクト内のすべてのビューにグローバルに適用されます。
  • 費用がかかる可能性がある応答の生成からあなたを救うことはありません。
  • HTTP GETリクエストにのみ適しています。

ここでは、特定の問題に最も適したツールを選択する必要があります。 ETagと変更時間をすばやく計算する方法があり、一部のビューでコンテンツの生成に時間がかかる場合は、このドキュメントで説明されているconditionデコレータの使用を検討する必要があります。 すべてがすでにかなり高速に実行されている場合は、ミドルウェアの使用に固執し、ビューが変更されていなければ、クライアントに返送されるネットワークトラフィックの量は引き続き削減されます。