引き続き、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ボタンを押すと例外が走り、以下のエラーメッセージが表示される。

これで投票に関する簡単なビューを構築することができた。