引き続き、Djangoのチュートリアルを進めていく。
前回までの記事はこちら。
今回は質問詳細ページのビューとなるdetail.htmlに、投票をおこなうためのフォームを実装する手順をまとめていく。
コンテンツ
テンプレートファイルの編集
まずはテンプレートファイル(templates/polls/detail.html)に以下のコードを書く。
<h1>{{ question.question_text }}</h1>
{% if error_message %}<p><strong>{{ error_message }}</strong></p>{% endif %}
<form action="{% url 'polls:vote' question.id %}" method="post">
{% csrf_token %}
{% for choice in question.choice_set.all %}
<input type="radio" name="choice" id="choice{{ forloop.counter }}" value="{{ choice.id }}">
<label for="choice{{ forloop.counter }}">{{ choice.choice_text }}</label><br>
{% endfor %}
<input type="submit" value="Vote">
</form>
途中で出てくるcsrf_tokenタグは、クロスサイトリクエストフォージェリ対策のもので、POST送信するフォームを作るときは必ず書くようにしておく。
フォームのactionには、投票ビューのURLを指定し、パラメータとして質問IDを渡すようにしている。
また補足として、forloop.counterはDjangoテンプレートのfor文で使用できるプロパティで、ループカウンターを取得することができる。
今回はこのカウンターを利用してフォームパーツ要素のIDとして設定した。
パラメータを受け取る投票ビューの構築
次に投票ビューの構築を進めていく。
polls/views.pyのvoteクラスを以下のとおり編集する。
from django.shortcuts import get_object_or_404, render
from django.http import HttpResponse, HttpResponseRedirect
from django.urls import reverse
from .models import Choice, Question
def vote(request, question_id):
question = get_object_or_404(Question, pk=question_id)
try:
selected_choice = question.choice_set.get(pk=request.POST['choice'])
except(KeyError, Choice.DoesNotExist):
return render(request, 'polls/detail.html', {
'question': question,
'error_message': "You didn't select a choice.",
})
else:
selected_choice.votes += 1
selected_choice.save()
return HttpResponseRedirect(reverse('polls:results', args=(question.id,)))
try文でPOST値を元に選択肢を取得しているが、この時もしPOST値としてchoiceを取得できなければKeyError例外へと処理が移り、error_messageを持たせて質問詳細ページへと画面を遷移させる。
try文が正常終了した場合はelse文へと処理が移り、選択肢の投票カウンターをインクリメントさせ、HttpResponseRedirectを返す。
投票後の結果ページのビューを作る
最後に投票が正常終了した時に遷移する、結果ページのビューを作る。
まずはpolls/views.pyのresultsクラスに以下のコードを書く。
def results(request, question_id):
question = get_object_or_404(Question, pk=question_id)
return render(request, 'polls/results.html', {'question': question})
最後にテンプレートとなるtemplates/polls/results.htmlに以下のコードを書く。
<h1>{{ question.question_text }}</h1>
<ul>
{% for choice in question.choice_set.all %}
<li>{{ choice.choice_text }} -- {{ choice.votes }} vote{{ choice.votes|pluralize }}</li>
{% endfor %}
</ul>
<a href="{% url 'polls:detail' question.id %}">Vote again?</a>
開発サーバーで動作確認
ここまで出来たら、python manage.py runserverコマンドで開発サーバーを起動し、動作確認をおこなってみよう。
以下の質問詳細ページへアクセスする。
http://127.0.0.1:8000/polls/1/
選択肢チェックボックスのいずれかにチェックを入れ、Voteボタンを押すと以下の結果ビューへと画面が遷移され、現在の投票結果が表示される。
なお、選択肢にチェックを入れずにVoteボタンを押すと例外が走り、以下のエラーメッセージが表示される。
これで投票に関する簡単なビューを構築することができた。