| この記事の要点 |
|
redirect() の 3 つの渡し方
from django.shortcuts import redirect
# 1. URL 名(推奨)
return redirect("blog:index")
return redirect("blog:detail", pk=article.pk)
# 2. モデルインスタンス(get_absolute_url が呼ばれる)
return redirect(article)
# 3. URL 文字列
return redirect("/blog/")
return redirect("https://example.com/")
URL 名で書けば、ルーティングを変えてもビューを直す必要がありません。urls.py の name= の付け方は aタグのhrefの記載方法 と同じ仕組みです。
POST の後は必ずリダイレクト
from django.contrib import messages
from django.shortcuts import redirect, render
def create(request):
if request.method == "POST":
form = ArticleForm(request.POST)
if form.is_valid():
article = form.save()
messages.success(request, "登録しました")
return redirect("blog:detail", pk=article.pk) # ← ここが重要
else:
form = ArticleForm()
return render(request, "blog/create.html", {"form": form})
保存後に render() で画面を返すと、ブラウザの再読み込みで同じ POST が再送されます。「登録ボタンを 1 回押したのに 2 件登録された」の典型的な原因です。
この「POST → Redirect → GET」の流れを PRG パターンと呼びます。バリデーションエラーのときだけ render() で戻し、成功時は必ずリダイレクトします。
クエリ文字列を付ける
from urllib.parse import urlencode
from django.urls import reverse
url = reverse("blog:index") + "?" + urlencode({"page": 2, "q": "検索語"})
return redirect(url)
redirect("blog:index", page=2) と書いても ?page=2 にはなりません。URL のパス部分の引数として解釈されるため、該当する path() が無ければ NoReverseMatch になります。
ステータスコードの違い
| コード | 意味 | Django での書き方 |
|---|---|---|
| 302 | 一時的(既定) | redirect(...) |
| 301 | 恒久的な移転 | redirect(..., permanent=True) |
| 303 | POST の後は GET で取り直せ | HttpResponseSeeOther |
| 307 / 308 | メソッドを変えずに転送 | 通常は使わない |
301 はブラウザに強くキャッシュされます。一度返すと、設定を戻してもユーザーの手元では古い転送先が使われ続けます。検索エンジンに移転を伝える目的以外では 302 を使ってください。
ログイン後に元のページへ戻す
from django.shortcuts import redirect
from django.utils.http import url_has_allowed_host_and_scheme
def login_view(request):
if request.method == "POST":
# ... 認証処理 ...
next_url = request.POST.get("next") or request.GET.get("next")
if next_url and url_has_allowed_host_and_scheme(
next_url,
allowed_hosts={request.get_host()},
require_https=request.is_secure(),
):
return redirect(next_url)
return redirect("home")
検証せずに redirect(request.GET["next"]) と書いてはいけません。攻撃者が ?next=https://evil.example/ を仕込んだリンクを配ると、自サイトのログインを経由して偽サイトへ誘導できてしまいます(オープンリダイレクト)。
クラスベースビューの場合
from django.urls import reverse_lazy
from django.views.generic import CreateView, RedirectView
class ArticleCreateView(CreateView):
model = Article
fields = ["title", "body"]
success_url = reverse_lazy("blog:index") # 固定の遷移先
# 動的に決めたいときはこちら
def get_success_url(self):
return reverse("blog:detail", kwargs={"pk": self.object.pk})
class OldPageView(RedirectView):
pattern_name = "blog:index"
permanent = True
クラス属性で URL を組み立てるときは reverse_lazy を使います。reverse だとモジュール読み込み時に評価され、URL 設定がまだ読まれていないため ImproperlyConfigured になります。
model に get_absolute_url() が定義されていれば success_url は省略できます。
そのほかのリダイレクト
from django.http import HttpResponseRedirect, HttpResponsePermanentRedirect
return HttpResponseRedirect("/blog/") # redirect() の中身はこれ
return HttpResponsePermanentRedirect("/blog/") # 301
# 直前のページへ戻す(Referer が無い場合に備えて既定値を用意する)
return redirect(request.META.get("HTTP_REFERER", "/"))
HTTP_REFERER は送られてこないことがあり、値も偽装できます。遷移先の決定に使う場合も、前節と同じ検証を通してください。
うまくいかないとき
| 症状 | 原因 |
|---|---|
| リダイレクトされない | redirect(...) を return していない |
NoReverseMatch | URL 名・名前空間・引数が合っていない |
ERR_TOO_MANY_REDIRECTS | 転送先が自分自身を指している。条件分岐を見直す |
| 末尾スラッシュで 1 回余分に転送される | APPEND_SLASH の働き。path() の定義に合わせる |
| 301 が消えない | ブラウザのキャッシュ。シークレットウィンドウで確認する |
| messages が表示されない | 遷移先のテンプレートで {% for m in messages %} を出していない |
関連
子ページはありません
人気ページ
- 1 Eclipseで「サーバーに追加または除去できるリソースがありません。」の原因と対処法
- 2 tomcat の起動 / 停止ログと catalina.log・catalina.out の違い
- 3 JavaScript で base URL を取得する方法|window.location.origin
- 4 YouTube Data API v3 エラー一覧|403・400・404 の原因と対処
- 5 Laravel エラー一覧|500/Blade/DB 接続/ルーティングの代表エラー
- 6 3Dグラフィックスのソフト一覧|Blender・Metasequoiaと用途別の選び方
- 7 Spring Frameworkのアノテーション一覧
- 8 【Spring】@Valueアノテーションとは
- 9 CATALINA_HOME の確認方法 (Linux / Mac)
- 10 【Spring】@Autowiredアノテーションとは
最近更新/作成されたページ
- Visual Studio「デザイナーを表示できません」エラーの原因と対処 2026-10-28 12:06:00
- SSH 認証に失敗しました。再試行してください の対処 2026-10-28 12:06:00
- SSH2 秘密鍵の読み込みに失敗しました の原因と対処 2026-10-28 12:06:00
- TeraTerm のよくあるエラーと対処法完全ガイド — Connection refused / 2026-10-28 12:06:00
- Visual Studio エラー一覧|コンパイルエラー・警告の確認方法と対処 2026-10-28 11:39:57
- Whitelabel Error Page の原因と消し方|Spring Boot のエラーページ 2026-10-28 11:39:57
- Eclipseで「サーバーに追加または除去できるリソースがありません。」の原因と対処法 2026-10-28 11:39:57
- SQL WHERE 句完全ガイド 2026-10-28 11:39:57
- SQL JOIN 完全ガイド (INNER/LEFT/RIGHT/FULL OUTER) 2026-10-28 11:39:57
- tomcat の起動 / 停止ログと catalina.log・catalina.out の違い 2026-10-28 10:24:23
- Tomcat の起動確認方法 5 つ|localhost:8080・ポート・プロセス・ログ 2026-10-28 10:24:23
- Oracle 19c COMMITの使い方: 暗黙コミット・自動確定・応答不明時の確認 2026-10-28 09:59:09
- Oracle 19c DELETE文|削除対象の確認・COMMIT前の判断・ROLLBACKの条件 2026-10-28 09:59:09
- Oracle 19c INSERT文|INSERT ALL・SELECT・RETURNINGとMERGEの使い分け 2026-10-28 09:59:09
- Oracle 19cのトランザクション制御|COMMIT・ROLLBACK・分離レベルと接続設定 2026-10-28 09:59:09