6.

Django CBVでテンプレートに値を渡す|get_context_data・extra_context

編集
この記事の要点
  • Django のクラスベースビュー(CBV)でテンプレートに変数を渡す基本は get_context_data() のオーバーライド
  • super().get_context_data(**kwargs) を呼び、辞書に追加して return
  • 簡単な定数なら extra_context 属性に dict を直書きするだけで OK
  • URL からの値は self.kwargs['...']、クエリ文字列は self.request.GET.get(...)
  • モデル一覧は ListView、詳細は DetailViewobject / object_list を自動で渡してくれる

クラスベースビュー (CBV) でテンプレートに値を渡す

Django のクラスベースビュー(Class-Based View, CBV)は、関数ベースビュー(FBV)よりも汎用ビュー(TemplateView / ListView / DetailView など)を継承して書くスタイルです。テンプレートに変数を渡したいときは get_context_data() をオーバーライドするのが基本パターンになります。

基本パターン: get_context_data

# views.py
from django.views.generic import TemplateView

class SampleView(TemplateView):
    template_name = "sample.html"

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context["title"] = "Sample Title"
        context["description"] = "This is a sample description."
        return context
{# templates/sample.html #}
<!DOCTYPE html>
<html>
<head><title>{{ title }}</title></head>
<body>
  <h1>{{ title }}</h1>
  <p>{{ description }}</p>
</body>
</html>

super().get_context_data(**kwargs) を必ず先に呼び出し、その辞書にキーを追加して return するのがポイントです。これを忘れると URL の名前付きキャプチャなど、Django が自動で詰める値が消えてしまいます。

簡単な値なら extra_context

動的に組み立てる必要のない静的な値だけなら、クラス属性 extra_context に dict を書くだけで十分です。

class SampleView(TemplateView):
    template_name = "sample.html"
    extra_context = {
        "title": "Sample Title",
        "description": "This is a sample description.",
    }

リストや辞書を渡す

class SampleView(TemplateView):
    template_name = "sample.html"

    def get_context_data(self, **kwargs):
        ctx = super().get_context_data(**kwargs)
        ctx["title"] = "Sample Title"
        ctx["items"] = ["item1", "item2", "item3"]
        ctx["user"] = {"name": "tarou", "age": 28}
        return ctx
<h1>{{ title }}</h1>

<ul>
  {% for item in items %}
    <li>{{ item }}</li>
  {% endfor %}
</ul>

<p>{{ user.name }} ({{ user.age }})</p>

URL から渡された値を取り出す

URL パターンに <int:pk> のような名前付きキャプチャがある場合、self.kwargs から取り出せます。

# urls.py
path("articles/<int:pk>/", views.ArticleView.as_view(), name="article"),

# views.py
class ArticleView(TemplateView):
    template_name = "article.html"

    def get_context_data(self, **kwargs):
        ctx = super().get_context_data(**kwargs)
        article_id = self.kwargs["pk"]
        ctx["article_id"] = article_id
        return ctx

クエリ文字列から取り出す

class SearchView(TemplateView):
    template_name = "search.html"

    def get_context_data(self, **kwargs):
        ctx = super().get_context_data(**kwargs)
        ctx["q"] = self.request.GET.get("q", "")
        return ctx

ListView / DetailView は自動で渡される

汎用ビューを継承すると、もっと楽に変数が渡せます。

from django.views.generic import ListView, DetailView
from .models import Article

class ArticleListView(ListView):
    model = Article
    template_name = "articles/list.html"
    context_object_name = "articles"   # デフォルトは object_list
    paginate_by = 20

class ArticleDetailView(DetailView):
    model = Article
    template_name = "articles/detail.html"
    context_object_name = "article"    # デフォルトは object

ListViewobject_list(または context_object_name で指定した名前)と paginator / page_obj を自動で渡し、DetailViewobject を渡します。

FormView でフォームを渡す

from django.views.generic.edit import FormView
from .forms import ContactForm

class ContactView(FormView):
    template_name = "contact.html"
    form_class = ContactForm
    success_url = "/thanks/"

    def get_context_data(self, **kwargs):
        ctx = super().get_context_data(**kwargs)
        ctx["page_title"] = "お問い合わせ"
        return ctx

    def form_valid(self, form):
        # 送信処理
        return super().form_valid(form)

FormView ではテンプレートに form という名前でフォームインスタンスが自動で渡ります。

よくある落とし穴

症状原因 / 対処
テンプレートに値が表示されないsuper().get_context_data(**kwargs) を呼んでいない / 戻り値を return していない
名前付きキャプチャが取れないsuper() を呼ばずに自分で dict を作っている
self.request が Noneas_view() 経由でないと self.request は設定されない(呼び方ミス)
extra_context が反映されないget_context_data を独自に上書きしているとマージされない場合がある
パフォーマンスが悪いget_context_data 内で重い処理をしている。get_queryset() で select_related / prefetch_related

関数ベースビュー (FBV) との比較

# FBV: シンプルだが定型コードが多くなりがち
def sample(request):
    return render(request, "sample.html", {
        "title": "Sample Title",
        "description": "This is a sample description.",
    })
項目FBVCBV
記述量シンプルクラス定義の分やや多い
再利用性低い継承・Mixin で高い
汎用機能(一覧/詳細/フォーム)自分で書くListView / DetailView / FormView
学習コスト低いやや高い(MRO の理解が必要)

関連

  • ビュー — 親カテゴリ
  • TemplateView / ListView / DetailView / FormView
  • get_context_data / get_queryset / get_object
  • Mixin(LoginRequiredMixin / UserPassesTestMixin)
  • テンプレートタグ・フィルタ
編集
Post Share
子ページ

子ページはありません

同階層のページ
  1. クラスベースビュー(主流)の作り方とviewの分割
  2. 関数ベースビューの作り方とviewの分割
  3. URLディスパッチャー(ルーティング処理)
  4. GETとPOSTパラメータ受け取り
  5. クラスベースビューでGET/POSTリクエストの受け取り方
  6. クラスベースビューでテンプレートに値を渡す方法
  7. ビューでリダイレクト
  8. cookieの値の設定と取得
  9. HTTPステータスコードの返し方

最近更新/作成されたページ