Closed7

Djangoのチュートリアルをなぞってみる その6:フォーム

suzuki-navisuzuki-navi

どこかでチュートリアルの手順を飛ばしたのか、Choiceにレコードがないので、管理画面からレコードを登録する。そのために以下の修正をする。

diff --git a/mysite/polls/admin.py b/mysite/polls/admin.py
index 6af8ff6..f191ae3 100644
--- a/mysite/polls/admin.py
+++ b/mysite/polls/admin.py
@@ -1,5 +1,6 @@
 from django.contrib import admin

-from .models import Question
+from . import models

-admin.site.register(Question)
+admin.site.register(models.Question)
+admin.site.register(models.Choice)
suzuki-navisuzuki-navi

以下のようにform要素を書く。

diff --git a/mysite/polls/templates/polls/detail.html b/mysite/polls/templates/polls/detail.html
index 975db2a..bfe30b3 100644
--- a/mysite/polls/templates/polls/detail.html
+++ b/mysite/polls/templates/polls/detail.html
@@ -1,6 +1,12 @@
-<h1>{{ question.question_text }}</h1>
-<ul>
-{% for choice in question.choice_set.all %}
-    <li>{{ choice.choice_text }}</li>
-{% endfor %}
-</ul>
+<form action="{% url 'polls:vote' question.id %}" method="post">
+    {% csrf_token %}
+    <fieldset>
+        <legend><h1>{{ question.question_text }}</h1></legend>
+        {% if error_message %}<p><strong>{{ error_message }}</strong></p>{% endif %}
+        {% 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 %}
+    </fieldset>
+    <input type="submit" value="Vote">
+</form>
diff --git a/mysite/polls/urls.py b/mysite/polls/urls.py
index fc78e3e..b121783 100644
--- a/mysite/polls/urls.py
+++ b/mysite/polls/urls.py
@@ -2,6 +2,7 @@ from django.urls import path

 from . import views

+app_name = "polls"
 urlpatterns = [
     # ex: /polls/
     path("", views.index, name="index"),

ブラウザでアクセスすると、こんなHTMLソースになっている。

<form action="/polls/1/vote/" method="post">
    <input type="hidden" name="csrfmiddlewaretoken" value="NGmB0zNVvOfsTcUIhRTsaJ5XNqAieLKX5HvoEC70J7A75QUQ5xINnjyzSYTqdGt2">
    <fieldset>
        <legend><h1>What&#x27;s up??</h1></legend>
            <input type="radio" name="choice" id="choice1" value="1">
            <label for="choice1">OK</label><br>
        
            <input type="radio" name="choice" id="choice2" value="2">
            <label for="choice2">No Good</label><br>
    </fieldset>
    <input type="submit" value="Vote">
</form>
suzuki-navisuzuki-navi

投票のPOST先を実装する。

diff --git a/mysite/polls/views.py b/mysite/polls/views.py
index d4ccd7e..d5d0943 100644
--- a/mysite/polls/views.py
+++ b/mysite/polls/views.py
@@ -1,6 +1,8 @@
-from django.http import HttpResponse, Http404
+from django.http import HttpResponse, Http404, HttpResponseRedirect
+from django.shortcuts import get_object_or_404, render
 from django.template import loader
-from .models import Question
+from django.urls import reverse
+from .models import Question, Choice

 def index(request):
     latest_question_list = Question.objects.order_by("-pub_date")[:5]
@@ -26,4 +28,24 @@ def results(request, question_id):
     return HttpResponse(response % question_id)

 def vote(request, question_id):
-    return HttpResponse("You're voting on question %s." % 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,)))

votesの値を取得してから1加算した値を保存しているので、同時アクセスしたらおかしくなりうる。

suzuki-navisuzuki-navi

投票結果のページを実装

diff --git a/mysite/polls/templates/polls/results.html b/mysite/polls/templates/polls/results.html
new file mode 100644
index 0000000..3b2c74f
--- /dev/null
+++ b/mysite/polls/templates/polls/results.html
@@ -0,0 +1,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>
diff --git a/mysite/polls/views.py b/mysite/polls/views.py
index d5d0943..29204d6 100644
--- a/mysite/polls/views.py
+++ b/mysite/polls/views.py
@@ -24,8 +24,8 @@ def detail(request, question_id):
     return HttpResponse(template.render(context, request))

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

 def vote(request, question_id):
     question = get_object_or_404(Question, pk=question_id)
このスクラップは2023/08/06にクローズされました