今回のテーマは「DetailViewを使った詳細表示画面の作成」です。前回に引き続きクラスベースビューの使い方を覚えていきましょう。DetailViewは名前の通り詳細情報を表示するビューです。今回は前回リスト表示したトピックの詳細を表示するページを作っていきましょう。
※本ページはDetailViewを使った詳細表示画面の作成まで読まれた方を対象としています。そのためサンプルソースコードが省略されている場合があります。
トピック詳細ページを作る
まずはtemplates/thread/detail_topic.htmlを作成します。
templates/thread/detail_topic.html
{% extends 'base/base.html' %}
{% block title %}トピック作成 - {{ block.super }}{% endblock %}
{% block content %}
<div class="ui grid stackable">
<div class="eleven wide column">
<div class="ui breadcrumb">
<a href="{% url 'base:top' %}" class="section">TOP</a>
<i class="right angle icon divider"></i>
<a class="section">{{topic.category.name}}</a>
<i class="right angle icon divider"></i>
<a class="active section">{{topic.title}}</a>
</div>
<div class="ui segment">
<div class="content">
<div class="header"><h3>{{topic.title}}</h3></div>
<p>{{topic.user_name}} - {{topic.created}}</p>
<div class="ui secondary segment">
<p><pre>{{topic.message}}</pre></p>
</div>
</div>
</div>
</div>
{% include 'base/sidebar.html' %}
</div>
{% endblock %}
次にthread/views.pyに追記しましょう。
thread/views.py
from django.shortcuts import render
from django.views.generic import DetailView
from . models import Topic
class TopicDetailView(DetailView):
"""トピック表示用クラス
DetailView使用例として作成
"""
template_name = 'thread/detail_topic.html'
model = Topic
context_object_name = 'topic'
非常に簡単ですね。DetailViewはtemplate_nameとmodelに値を渡してあげると、URLで渡されたpk(primary key)に対応したオブジェクトを呼び出してテンプレートに渡してくれます。その際、例えばTopicオブジェクトならばtopicという名前で渡されます。もしテンプレートに渡す名前を指定したい場合はcontext_object_nameで指定します。今回の場合はcontext_object_nameがなくても問題なく機能しますが、できるだけcontext_object_nameは書いたほうが良いと思います。
URLを作成する
ページにアクセスするURLを決めましょう。{domain_root}/thread/{トピックID}というURLでアクセス出来るようにしましょう。thread/urls.pyを以下のようにします。
thread/urls.py
from django.urls import path
from . import views
app_name = 'thread'
urlpatterns = [
path('/<int:pk>/', views.TopicDetailView.as_view(), name='topic'),
]
ここで<int:pk>
がポイントです。URLからpkを渡すことでDetailViewがpkを元にオブジェクトを取得してくれます。
ブラウザで確認してみましょう。localhost:8080/thread/1にアクセスしてます。番号はトピックのIDであればOKです。
さて、ここで読者諸氏はTemplateViewクラスや関数で書いた場合にどうなるか興味があると思いますので、記載例を見てみましょう。 まずTemplateViewで書いた場合です。
thread/views.py(一部抜粋)
from django.shortcuts import render
from django.views.generic import DetailView, TemplateView
from . models import Topic
class TopicDetailView(DetailView):
# ...省略
class TopicTemplateView(TemplateView):
"""トピック表示用クラス
TemplateViewを使った場合の例として作成
"""
template_name = 'thread/detail_topic.html'
def get_context_data(self, **kwargs):
ctx = super().get_context_data(**kwargs)
ctx['topic'] = get_object_or_404(Topic, id=self.kwargs.get('pk', ''))
return ctx
オブジェクトの取得とテンプレートへの受け渡しをget_context_dataをオーバーライドして行う必要があります。この際に、不適切なpkが渡された場合にはNot found 404を返すようにget_object_or_404関数を使用します。これはよく使う手法です。importも忘れないようにして下さいね。また、DetailViewでは自動的に処理されていたpkがself.kwargsから取得している点も注目して下さい。pkから渡されたパラメータはpkに関わらず、このように取り出すことが出来ます。 次に関数ベースで書いた場合ですが、以下例のようになります。
thread/views.py(一部抜粋)
from django.shortcuts import render, get_object_or_404
from django.views.generic import DetailView, TemplateView
from . models import Topic
class TopicDetailView(DetailView):
# ...省略
class TopicTemplateView(TemplateView):
# ...省略
def detail_topic(request, pk):
"""トピック表示用関数
DetailViewを関数ベースで実装する例
"""
ctx = {}
template_name = 'thread/detail_topic.html'
if request.method == 'GET':
ctx['topic'] = get_object_or_404(Topic, id=pk)
return render(request, template_name, ctx)
特に問題はないと思いますが、pkの取得の仕方が、変わっている点に注意して下さい。
最後に、ここの本題ではないですが、トップページの各トピックへのリンクを修正しておきましょう。
templates/base/top.html(一部抜粋)
... 省略
{% for topic in topic_list %}
<div class="item">
<div class="content">
<div class="header">
<a href="{% url 'thread:topic' pk=topic.id %}"><h4>{{topic.title}}</h4></a>
</div>
<div class="meta">
<span class="name">{{topic.user_name}}</span>
<span class="date">{{topic.created}}</span>
</div>
</div>
</div>
{% endfor %}
... 省略
引数をpk=topic.idで渡しているところがポイントですね。これでトップページから各トピックにアクセスすることが出来るようになりました。
最後に
ここまで初期投入したデータや管理画面から入力したデータを表示することを見てきました。次回からはユーザーがデータを登録できる画面を作っていきますよ。
Sponsored Link
トピック詳細ページを作るところで
TemplateDoesNotExist at /thread/1
というエラーがでます。
from django.shortcuts import render, redirect
from django.views.generic import CreateView, FormView, DetailView
from . models import Topic
class TopicDetailView(DetailView):
template_name = ‘thread/detail.html’
model = Topic
context_object_name = ‘topic’
こちらですが thread/detail_topic.htmlでしょうか?
MKさん
ご報告ありがとうございます。
ご指摘の通りdetail_topic.htmlの誤記です。修正しておきます。
ありがとうございます。
こちらの教材すごくいいです。
普段はLaravelを使っているのですが
趣味でDjangoをやりたいと思いこちらのサイトにいきつきました。
章を進んで調べても解決しない場合また、クロ様に連絡します。
MKさん
ありがとうございます。少しでもお役に立てれば幸いです。Djangoを使える力が身に付くように更に分かりやすい記事を書きたいと思います。