はじめに
Django を学習するには、何はともあれ公式チュートリアル「はじめての Django アプリ作成」からスタートしてみるのが良いでしょう。しかし、いざチュートリアル通りに進めようとしても躓く場合も多いと思います。そこで、「チュートリアルの進め方」シリーズとして、チュートリアルに沿った作業を行いながら補足事項をまとめることにしました。チュートリアルで躓いた際のヒントとしてお使いください。
利用環境は Ubuntu、 Linux Mint を中心として説明しますが、 Windows についても補足します。
前回は、Django チュートリアルの進め方 その 3 (2) と題してはじめての Django アプリ作成、その 3 の後半についてまとめました。
今回は、はじめての Django アプリ作成、その 4 の前半についてまとめました。
簡単なフォームを書く
polls/tetail.html を修正して <form> を挿入します。
1 2 3 4 5 6 7 8 9 10 11 12 |
<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> |
表示してみます。http://127.0.0.1:8000/polls/1/ にアクセスします。
質問 (What' up?) に対する答えの選択 (Not much / The sky) と、投票 (Vote) ができるようになっています。
Vote ボタンで実行されるビュー vote() を修正します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
from django.http import HttpResponse, HttpResponseRedirect from django.shortcuts import get_object_or_404, render from django.urls import reverse from .models import 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): # Redisplay the question voting form. return render(request, 'polls/detail.html', { 'question': question, 'error_message': "You didn't select 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. return HttpResponseRedirect(reverse('polls:results', args=(question.id,))) |
投票結果を表示するビュー (results) を定義します。
1 2 3 |
def results(request, question_id): question = get_object_or_404(Question, pk=question_id) return render(request, 'polls/results.html', {'question': question}) |
results のテンプレートを記述します。
1 2 3 4 5 6 7 8 9 |
<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/ にアクセスします。
Not much を選択してみます。
投票 (Vote を押す) してみます。
Not much に一票入っています。Vote again? を押して、開いた投票画面から今度は The sky を投票してみます。
The sky にも一票入りました。
Vote again? を押してから、何も選択せずに投票してみます。
エラーが表示されました。何も選択せずに投票したらエラーという事です。
ただし、上記はデバッグ時の画面となります。settings.py で Debug=False とした場合は下記の画面になります。
vote ビューの記述には、チュートリアルの注釈にあるように問題点があります。
selected_choice = question.choice_set.get(pk=request.POST['choice']) で取得した値をインクリメントして DB に保存するまでの間に間があるため、その間に別の投票者が selected_choice を取得して投票すると、後から投票した人の投票結果で上書きされてしまいます。
1 2 3 4 5 6 7 |
def vote(request, question_id): . . selected_choice = question.choice_set.get(pk=request.POST['choice']) . . selected_choice.save() |
気になる人は下記のように修正しておきましょう。(Avoiding race conditions using F() 参照)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
from django.http import HttpResponse, HttpResponseRedirect from django.shortcuts import get_object_or_404, render from django.urls import reverse from django.db.models import F from .models import 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): # Redisplay the question voting form. return render(request, 'polls/detail.html', { 'question': question, 'error_message': "You didn't select a choice.", }) else: selected_choice.votes = F('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. return HttpResponseRedirect(reverse('polls:results', args=(question.id,))) |
21行目のように F式を使うと、Python のコードとして演算が行われずに、SQL レベルで ( 'votes' フィールドに対して) +1 の演算が行われるため、上記問題を回避できます。これは Django の仕組みです。
次回は はじめての Django アプリ作成、その 4 の後半についてまとめます。
参考
-
- Django チュートリアルの進め方 その 1
- Django チュートリアルの進め方 その 2 (1)
- Django チュートリアルの進め方 その 2 (2)
- Django チュートリアルの進め方 その 2 (3)
- Django チュートリアルの進め方 その 3 (1)
- Django チュートリアルの進め方 その 3 (2)
- Django チュートリアルの進め方 その 4 (1)
- Django チュートリアルの進め方 その 4 (2)
- Django チュートリアルの進め方 その 5 (1)
- Django チュートリアルの進め方 その 5 (2)
- Django チュートリアルの進め方 その 6
- Django チュートリアルの進め方 その 7