5. はじめての Django アプリ作成、その 4
出典:はじめての Django アプリ作成、その 4 | Django ドキュメント | Django
https://docs.djangoproject.com/ja/3.2/intro/tutorial04/
5.1. 簡単なフォームを書く
5.1.1. detail テンプレートの編集
フォーム処理を追加するため、 polls/detail.html に form タグを追加します。
<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
はクロスサイトリクエストフォージェリ(cross-site request forgeries)対策のタグです。POSTフォームにはとりあえず入れておくといいみたい。
forloop.counter
は for ループが何周目かを表す値が入ってます。
form の action は {% url ‘polls:vote’ question.id %} に設定されています。これはすでに polls/urls.py に定義されています。
path('<int:question_id>/vote/', views.vote, name='vote'),
http://127.0.0.1:8000/polls/1/ にアクセスすると以下のように表示されます。
5.1.2. views.vote の編集
views.vote (views.py の vote メソッド)は以下のように書き換えます。
from django.http import HttpResponse, HttpResponseRedirect # HttpResponseRedirect を追加
from django.shortcuts import get_object_or_404, render
from django.urls import reverse #追加
# Choice と Question オブジェクトの読み込み
from .models import Choice, Question # Choice を追加
#(省略)
# 投票 ページ
def vote(request, question_id):
# return HttpResponse("You're vooting on question %s." % question_id)
# question を取得
question = get_object_or_404(Question, pk=question_id)
try:
# question に紐付いている Choice から POST['choice'] と同じ番号を持つ Choice を取得。
# 存在しなかった場合は KeyError が返され、except に流される。
selected_choice = question.choice_set.get(pk=request.POST['choice'])
except (KeyError, Choice.DoseNotExist):
# redisplay the question voting form.
# エラーメッセージとともに質問投票フォームに戻します。
return render(request, 'polls/detail.html', {
'question': question,
'error_message': "You didn't selecte a choice.",
})
else:
selected_choice.votes += 1
selected_choice.save()
# Always return an HttpResponseRedirect after successfully dealing
# with POST data. This prevents data from being posted twice if a
# user hits the Back button.
# POSTデータを正常に処理したあとは常に HttpResponseRedirect を返します。
# これによりユーザーが戻るボタンを押した場合、データが2回投稿されるのを防ぎます。
return HttpResponseRedirect(reverse('polls:results', args=(question.id,)))
reverse は与えられた view の名前から相当する url を返す django の関数です。ここでは question.id を引数とした polls:results の url を返しています。 たとえば question.id が 1 だった場合は /polls/1/results/ が返されます。
5.1.3. views.results の編集
リダイレクト先の results のビューを編集します。指定しているテンプレートが違うだけであとは detail とほぼ同じです。
#(省略)
# 結果 ページ
def results(request, question_id):
# response = "You're looking at the results of question %s."
# return HttpResponse(response % question_id)
question = get_object_or_404(Question, pk=question_id)
return render(request, 'polls/results.html', {'question': question})
#(省略)
5.1.4. results テンプレートの作成
テンプレート 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>
これで投票と結果の表示ができるようになりました。
http://127.0.0.1:8000/polls/1/ から投票すると結果画面が表示されます。
5.2. 汎用ビューを使う: コードが少ないのはいいことだ
detail ビューと results ビューは似たような内容なので、汎用ビュー(generic view)を使って書き換えます。ついでに index ビューも書き換えます。
5.2.1. URLconf の修正
from django.urls import path
from . import views
app_name = 'polls'
urlpatterns = [
# ex: /polls/
# path('', views.index, name='index'),
path('', views.IndexView.as_view(), name='index'),
#ex: /polls/5/
# path('<int:question_id>/', views.detail, name='detail'),
path('<int:pk>/', views.DetailView.as_view(), name='detail'),
#ex: /polls/5/results/
# path('<int:question_id>/results/', views.results, name='results'),
path('<int:pk>/results/', views.ResultsView.as_view(), name='results'),
#ex: /polls/5/vote/
# path('<int:question_id>/vote/', views.vote, name='vote'),
path('<int:question_id>/vote/', views.vote, name='vote'),
]
5.2.2. views の修正
from django.http import HttpResponseRedirect
from django.shortcuts import get_object_or_404, render
from django.urls import reverse
from django.views import generic
# Choice と Question オブジェクトの読み込み
from .models import Choice, Question
# インデックス ページ
class IndexView(generic.ListView):
# List View では model か queryset の指定が必須になっており、
# また get_queryset によって取得した queryset の値は
# model の結果を上書きする。このためここでは
# model = question と記述されてなくても queryset から
# <model name> として question がセットされる。
# テンプレートの指定
# デフォルトは <app name>/<model name>_list.html
template_name = 'polls/index.html'
# コンテキスト変数名の指定
# デフォルトは <model name>_list
context_object_name = 'latest_question_list'
# 表示する queryset を設定
def get_queryset(self):
"""Return the last five published questions."""
return Question.objects.order_by('-pub_date')[:5]
# def index(request):
# latest_question_list = Question.objects.order_by('-pub_date')[:5]
# context = {
# 'latest_question_list': latest_question_list,
# }
# #return HttpResponse(template.render(context, request))
# return render(request, 'polls/index.html', context)
# 詳細 ページ
class DetailView(generic.DetailView):
model = Question
template_name = 'polls/detail.html'
# def detail(request, question_id):
# question = get_object_or_404(Question, pk=question_id)
# return render(request, 'polls/detail.html', {'question': question})
# 結果 ページ
class ResultsView(generic.DetailView):
model = Question
template_name = 'polls/results.html'
# def results(request, question_id):
# question = get_object_or_404(Question, pk=question_id)
# return render(request, 'polls/results.html', {'question': question})
# 投票 ページ
def vote(request, question_id):
#(省略)
以上で投票アプリが一通り完成しました。