▶ ユーザー画面を作成する基本的な仕組みを解説した記事はこちらをどうぞ
▶ HTMLに条件分岐を埋め込んだり、変数の中身を表示させる方法はこちらを参考にしてください。
はじめに
前回は、真っ白だったpolls/index.htmlの画面に「質問」の一覧を表示させるとところまでをおこないました。今回はappに機能を追加していくことにします。テンプレートの表示やデータの受け渡しなどの設定が必要で、すこし複雑にみえますが、一つ一つ処理をしていくことで作り上げることができます。まずはボタンを表示するところからやっていきます。
投票する機能を追加する(前編)
まずはテンプレートにボタンを追加するところから始めましょう。対象のファイルは、「pollster/templates/polls/index.html」です。
投票するボタンを追加する
for文の中の、質問の一覧を表示する部分を次のようにします。
(pollster/templates/polls/index.html)
<p class="lead">{{ question.question_text }}</p>
<a href="{%url 'polls:detail' question.id %}" class="btn-primary btn-sm">投票する</a>
aタグの「”{%url ‘polls:detail’ question.id %}”」の部分は、pollsアプリケーションのdetailと名前が付いているリンクに、「question」のid属性をくっつけたURLという意味になります。
ただ、このままではまだエラー(NoReverseMatch)がでます。これは逆引きのエラーです。ここで指定したURLが認識されるように「polls/urls.py」を修正する必要があります。次のようにします。
(polls/urls.py)
from django.urls import path
from . import views
app_name = 'polls'
urlpatterns = [
path('',views.index,name='index'),
path('<int:question_id>/',views.detail,name='detail'),
path('<int:question_id>/results/',views.results,name='results'),
path('<int:question_id>/vote/',views.vote,name='vote')
]
app_name=’polls’の部分が先のindex.htmlの「pollsというアプリケーションの~」に対応しています。その下のurlpatternsのところで、「 path(‘<int:question_id>/’,views.detail,name=’detail’) 」と記載している部分のname=’detail’がindex.htmlの「detailと名前が付いているリンク」に対応します。
これでURL自体は認識されるようなりました。ただ、detail.htmlを作っていないので、「detail.htmlがない」というサーバーエラーとなります。そこで、detail.htmlを作り、このテンプレートが呼び出されるように「polls/views.py」にdetail関数を定義します。
(polls/views.py)
def detail(request):
return render(request, 'polls/detail.html')
これでエラーは消えてリンクを認識された状態になりました。
但し、リンク先はまだエラー(Type Error)になります。これはQuestion_idが認識できないために起こっています。
これは、polls/urls.pyのurlpatternsに「 path(‘<int:question_id>/’,views.detail,name=’detail’) 」のようにちゃんと記載していますが、views.pyのdetailの関数では、question_idは渡されていません。そこで、views.pyを次のように書き換えます。
(pollster/polls/views.py)
def detail(request,question_id):
return render(request,'polls/detail.html')
これでリンク先のエラーも消すことができました。(但し、リンク先はまだ真っ白です)これはdetail.htmlに何も書いていないためで、この何も書いていないテンプレートが呼び出されたからです。
パラメータをつけたリンクのページをつくる
pollster/polls/views.pyをさらに編集して、ページを作っていきます。
(pollster/polls/views.py)
def detail(request,question_id):
try:
question = Question.objects.get(pk=question_id)
except Question.DoesNotExist:
raise Http404("その質問はありません")
context = {'question':question}
return render(request,'polls/detail.html',context)
これまでQuestion.objects.all()ですべての質問を取得していたが、ここではある指定されたQuestionだけでよいため、getを使う。getを使うときは、存在しないものが指定されたときを想定する必要がある。その部分をtryとexceptで記述している。
contextにquestionをキーとしてquestionを渡し、detailが呼ばれた時のreturnの第3引数にcontextを指定したため、detail.htmlではquestionを使うことができる。
(pollster/templates/polls/detail.html)
{% extends 'base.html' %}
{% block content %}
<a class="btn btn-secondary btn-sm mb-3" href="{% url 'polls:index' %}">戻る</a>
<h1 class="text-center mb-3">{{ question.question_text }}</h1>
{% if error_message %}
<p class="alert alert-danger">
<strong>{{ error_message}}</strong>
</p>
{%endif%}
<form action="{% url 'polls:vote' question.id %}" method="POST">
{%csrf_token%}
{% for choice in question.choice_set.all %}
<div class="form-check">
<input type="radio" name="choice" class="form-check-input" id="choice{{ forloop.counter}}" value="{{choice.id}}"/>
<label for="choice{{ forloop.counter}}">{{choice.choice_text}}</label>
</div>
{% endfor %}
<input type="submit" value="投票" class="btn btn-success btn-lg btn-block mt-4" />
</form>
{% endblock %}
これでこの表示となります。
まず、戻るボタンを定義してるのは、「 <a class=”btn btn-secondary btn-sm mb-3″ href=”{% url ‘polls:index’ %}”>戻る</a> 」の部分となります。リンク先URLを前画面であるpollsアプリのindexとしていています。
次に「 {% if error_message %} 」の部分は、テンプレートが呼び出されたときにエラーメッセージがあるときの対応です。
<form>タグでは選択肢を書いていきます。1点、formタグで囲んだ部分には、 {%csrf_token%} をいれることを忘れないようにしてください。これはセッションハイジャックされないようにする、セキュリティ上必要な記述になります。
あとはfor文で選択肢を取り出して、inputタイプをradioにして表示しています。「 {% for choice in question.choice_set.all %} 」のchoice_set.allの部分は、従属関係のあるモデルにおいて自動で作成されるメソッドであるchoice_set.all()のことです。djangoの中では()が不要となる、という文法です。これで選択肢を一つ一つ取り出しています。
idの「 id=”choice{{ forloop.counter}}” 」のところは、djangoのテンプレートの中で使える特殊なキーワードです。forloopには、counterという属性があって、forループの中の何番目であるか、が格納されています。
まとめ
今回は、ボタンを設置してそのボタンのリンクを動的につくりました。整理するとやっていることは簡単なのですが、初めて目にすると難解に感じますよね。僕もまだ消化しきれていないです。しっかり復習しましょう。
▶ djangoの簡単なまとめ記事もあります。
コメント