Open1

なぜFBVはredirect、CBVはreverse_lazyなのか?Djangoの評価タイミングを理解する

tech_mwtech_mw

Djangoでビューから別のページにリダイレクトしたい場合、redirect()reverse_lazy() を使うことが多いですよね。

でも、

「関数ベースビュー(FBV)では redirect() が使えるのに、クラスベースビュー(CBV)では reverse_lazy() が必要」

これってなぜ?という疑問。

結論:評価タイミングと戻り値の違い

  • FBV(関数ビュー)**は「後から評価される」ため、redirect() を使っても問題なし
  • CBV(クラスベースビュー)は「先に評価される」ため、reverse_lazy() で評価を遅らせる必要がある
  • さらに、CBVの success_url はURL文字列(str)を期待しているため、レスポンスを返す redirect() は型的にも不適切

Djangoの処理順(図解)

以下は Django アプリがサーバー起動時〜リクエスト処理のざっくりとした内部処理の流れです。

[サーバー起動時(import時)]

1. Django が urls.py を読み込む
     ↓
2. views.py を import(ここで class も評価される)
     ├─ def my_view → 関数として「定義」されるだけ 
     └─ class MyView → class属性(success_urlなど)が評価
                  └── ここで redirect('home') が実行される ❌
3. urls.py の path('home', ...) がようやく登録される

[リクエスト時(実行時)]

・ FBVの場合、def my_view が呼び出されて、redirect('home')
→ redirect() 実行。この時には path はすでに登録済
    
・ CBVの場合、success_url に reverse_lazy('home') を指定
→ この時点で URL解決が実行される(遅延評価)

関数ベースビュー(FBV)の場合

def my_view(request):
    return redirect('home')

redirect('home') は実行時(リクエスト時)に評価されます。
この段階では urls.py のパス定義はすでに読み込まれているため、安全。

クラスベースビュー(CBV)の場合

class MyView(CreateView):
    success_url = reverse_lazy('home')  # ✅ OK(遅延評価)

class MyView(CreateView):
    success_url = redirect('home')  #  ❌ NG(評価が早すぎる+型が違う)

・上の reverse_lazy('home') は遅延評価されるため、リクエスト時に安全に URL を解決できます。
・一方 redirect('home') はその場で評価され、内部で reverse('home') を実行するため、views.py の import 時点では URL がまだ未登録の可能性があり危険です。
・さらに redirect() は HttpResponseRedirect オブジェクト(レスポンス)を返す関数のため、URL文字列が必要な success_url に対して不適切です。

結論ふたたび

・redirect() は 即時評価 & レスポンス返却
・reverse_lazy() は 遅延評価 & URL文字列を返す
→ よって、クラスベースビューには reverse_lazy() を使うべき