('

Django テンプレートで URL を取得する方法:request.path vs path_info vs get_full_path vs build_absolute_uri

\n

Django テンプレートで現在の URL が必要になる場面は多々あります。

\n
    \n
  • ナビゲーションで「現在のメニューをアクティブ化」
  • \n
  • ログイン後に「元のページへ戻る(next)」
  • \n
  • canonical URL / 共有リンクを作成
  • \n
  • クエリストリングを含めて「現在のページそのまま」をリンクにする
  • \n
\n

しかし {{ request.path }} 以外にも似たようなものがいくつかあるため、混乱しやすいです。 今日は混乱しやすい以下の 4 つを整理して比較します。

\n
    \n
  • request.path
  • \n
  • request.path_info
  • \n
  • request.get_full_path()
  • \n
  • request.build_absolute_uri()
  • \n
\n
\n

{{ request }} はどこから来るのか

\n

テンプレートで {{ request }} が見えるということは、通常 request context processor が有効になっていることを意味します。

\n
    \n
  • django.template.context_processors.requestテンプレートコンテキストに request を注入 します。
  • \n
  • このコンテキストプロセッサは TEMPLATES 設定の OPTIONS[\'context_processors\'] に登録されているときに動作します。
  • \n
\n

つまり、Django は settings.TEMPLATES に request をグローバルコンテキストとして入れるようにデフォルトで設定されているため、開発者が意図的にこの設定を削除しない限り、Django ユーザーは不便なく自然に使用できます。

\n

そして request オブジェクト自体は、Django がリクエストを受け取るときに HttpRequest(実際には WSGI/ASGI リクエストサブクラス)を作成し、ビューまで渡すオブジェクトです。このオブジェクトには pathpath_info などの属性が含まれます。

\n

Djangoという名前のマジシャンがリクエスト帽子から道具を取り出すイメージ

\n
\n

一行結論:4 つを覚えると便利

\n
    \n
  • path:ドメインを除いた「パスのみ」
  • \n
  • path_info:アプリが見る「実際のパス(スクリプトプレフィックス除外)」
  • \n
  • get_full_path()path + クエリストリング
  • \n
  • build_absolute_uri():スキーム+ホスト+パスまで全て(完全な URL)
  • \n
\n

それではそれぞれを正確に見ていきましょう。

\n
\n

1) request.path:ドメインなしの「パス」

\n

request.pathドメイン/スキームなし でリクエストパスだけを返します。例は以下のような形です。

\n
    \n
  • /music/bands/the_beatles/
  • \n
\n

いつ役立つか

\n
    \n
  • メニューのアクティブ化など単純比較
  • \n
\n

django\n {% if request.path == "/settings/" %}active{% endif %}\n* 「このページはどのカテゴリ?」といったプレフィックス比較

\n

django\n {% if request.path|slice:":5" == "/api/" %}...{% endif %}

\n
\n

2) request.path_info:デプロイ環境に左右されにくい「実際のパス」

\n

Django ドキュメントのポイントは次のとおりです。

\n
    \n
  • あるサーバ設定では URL パスが スクリプトプレフィックス(SCRIPT_NAME)path info に分かれます。
  • \n
  • path_info はその中で path info 部分を常に含む(つまり環境に依存しにくい)
  • \n
\n

簡単に言えば、アプリが /app などのプレフィックス下にマウントされる構成(リバースプロキシ、サブパスデプロイなど)では pathpath_info が異なる場合があります。

\n

いつ役立つか

\n
    \n
  • サブパスデプロイ(例:/app/ 以下でサービス)などの環境を考慮し「アプリ基準のパス」で判断したいとき
  • \n
  • テスト/本番サーバでプレフィックスが変わる場合
  • \n
\n
\n

通常は単一ドメインルート(/)で動かすと pathpath_info が同じになるため差を感じにくいです。\nしかし環境が変わると差が意味を持ちます。

\n
\n
\n

3) request.get_full_path()path + クエリストリングまで

\n

get_full_path()path にクエリストリングを付けた値 を返します。例は以下のような形です。

\n
    \n
  • /music/bands/the_beatles/?print=true
  • \n
\n

いつ役立つか

\n
    \n
  • 「現在のページそのままを共有/リフレッシュ/戻る」リンクが必要なとき
  • \n
\n

django\n <a href="{{ request.get_full_path }}">リフレッシュリンク</a>\n* ログイン/権限チェック後に元のページへ戻る next を作るとき

\n

django\n <a href="/login/?next={{ request.get_full_path|urlencode }}">ログイン</a>

\n
\n

ちなみに Django には get_full_path() 以外に get_full_path_info() もあります。\n後者は path_info ベースで動作します。今日の比較対象はではありませんが、差を知っておくと便利です。

\n
\n
\n

4) request.build_absolute_uri():スキーム+ドメインまで含む「完全な URL」

\n

build_absolute_uri() は現在のリクエストを基に 絶対 URL(absolute URI) を作成します。

\n

ざっくりこんな形になります。

\n
    \n
  • https://example.com/music/bands/the_beatles/?print=true
  • \n
\n

いつ役立つか

\n
    \n
  • メール/メッセンジャー共有リンクのように ドメインが必須 な場合
  • \n
  • canonical URL、og:url などのメタタグ
  • \n
  • 外部システムにコールバック URL を渡す
  • \n
\n

注意すべき点

\n

build_absolute_uri() はホストを作る際にリクエストの Host 情報に依存します(内部的に get_host() を使用)。\n実際にブラウザから来た URL ではありません。\n多くの普通の状況では get_host() の値と実際の URL ドメインが一致することもありますが、Nginx がリバースプロキシである場合や Django ミドルウェアで開発者が意図的に get_host() の値を別のテナントホストに設定するロジックを作った場合、{{ request.build_absolute_uri }} の値が常に実際のブラウザ URL と一致するとは限りません。

\n

したがって、nginx でも Django アプリ内部でもドメイン関連のロジックを実装している場合は、予想と異なるドメイン/スキームで生成される可能性があるので、デプロイ設定も併せて確認することをおすすめします。

\n
\n

結論とまとめ

\n
    \n
  • テンプレートでメニューをアクティブ化request.path
  • \n
  • サブパスデプロイ/プロキシ環境まで安定して比較request.path_info
  • \n
  • クエリストリングまで含めて「現在のページそのまま」request.get_full_path()
  • \n
  • 外部へ出る完全なリンク(ドメイン含む)request.build_absolute_uri()
  • \n
\n
\n

関連記事を見る

\n', '/media/editor_temp/6/c112847e-a9ca-4d58-9a3f-076a20bfbef7.png')