※弊社記事はたぶんにPRが含まれますので
話半分で受け取ってください。

Django公式サイトのチュートリアルを咀嚼しながらやってみる

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):

#(省略)

 以上で投票アプリが一通り完成しました。

ページ: 1 2 3 4 5 6 7 8

関連する記事