Web

Python初心者向け:django~ユーザー画面の作成③~

スポンサーリンク


▶ ユーザー画面を作成する基本的な仕組みを解説した記事はこちらをどうぞ

▶ HTMLに条件分岐を埋め込んだり、変数の中身を表示させる方法はこちらを参考にしてください。

はじめに

前回は、真っ白だったpolls/index.htmlの画面に「質問」の一覧を表示させるとところまでをおこないました。今回はappに機能を追加していくことにします。テンプレートの表示やデータの受け渡しなどの設定が必要で、すこし複雑にみえますが、一つ一つ処理をしていくことで作り上げることができます。まずはボタンを表示するところからやっていきます。

投票する機能を追加する(前編)

まずはテンプレートにボタンを追加するところから始めましょう。対象のファイルは、「pollster/templates/polls/index.html」です。

投票するボタンを追加する

for文の中の、質問の一覧を表示する部分を次のようにします。

(pollster/templates/polls/index.html)
<p class="lead">{{ question.question_text }}</p>
<a href="{%url 'polls:detail' question.id %}" class="btn-primary btn-sm">投票する</a>

aタグの「”{%url ‘polls:detail’ question.id %}”」の部分は、pollsアプリケーションのdetailと名前が付いているリンクに、「question」のid属性をくっつけたURLという意味になります。

ただ、このままではまだエラー(NoReverseMatch)がでます。これは逆引きのエラーです。ここで指定したURLが認識されるように「polls/urls.py」を修正する必要があります。次のようにします。

(polls/urls.py)
from django.urls import path
from . import views

app_name = 'polls'
urlpatterns = [
    path('',views.index,name='index'),
    path('<int:question_id>/',views.detail,name='detail'),
    path('<int:question_id>/results/',views.results,name='results'),
    path('<int:question_id>/vote/',views.vote,name='vote')
]

app_name=’polls’の部分が先のindex.htmlの「pollsというアプリケーションの~」に対応しています。その下のurlpatternsのところで、「 path(‘<int:question_id>/’,views.detail,name=’detail’) 」と記載している部分のname=’detail’がindex.htmlの「detailと名前が付いているリンク」に対応します。

これでURL自体は認識されるようなりました。ただ、detail.htmlを作っていないので、「detail.htmlがない」というサーバーエラーとなります。そこで、detail.htmlを作り、このテンプレートが呼び出されるように「polls/views.py」にdetail関数を定義します。

(polls/views.py)
def detail(request):
    return render(request, 'polls/detail.html')

これでエラーは消えてリンクを認識された状態になりました。

但し、リンク先はまだエラー(Type Error)になります。これはQuestion_idが認識できないために起こっています。

これは、polls/urls.pyのurlpatternsに「 path(‘<int:question_id>/’,views.detail,name=’detail’) 」のようにちゃんと記載していますが、views.pyのdetailの関数では、question_idは渡されていません。そこで、views.pyを次のように書き換えます。

(pollster/polls/views.py)
def detail(request,question_id):
    return render(request,'polls/detail.html')

これでリンク先のエラーも消すことができました。(但し、リンク先はまだ真っ白です)これはdetail.htmlに何も書いていないためで、この何も書いていないテンプレートが呼び出されたからです。

パラメータをつけたリンクのページをつくる

pollster/polls/views.pyをさらに編集して、ページを作っていきます。

(pollster/polls/views.py)
def detail(request,question_id):
    try:
        question = Question.objects.get(pk=question_id)
    except Question.DoesNotExist:
        raise Http404("その質問はありません")
    context = {'question':question}
    return render(request,'polls/detail.html',context)

これまでQuestion.objects.all()ですべての質問を取得していたが、ここではある指定されたQuestionだけでよいため、getを使う。getを使うときは、存在しないものが指定されたときを想定する必要がある。その部分をtryとexceptで記述している。

contextにquestionをキーとしてquestionを渡し、detailが呼ばれた時のreturnの第3引数にcontextを指定したため、detail.htmlではquestionを使うことができる。

(pollster/templates/polls/detail.html)
{% extends 'base.html' %}
{% block content %}
<a class="btn btn-secondary btn-sm mb-3" href="{% url 'polls:index' %}">戻る</a>
<h1 class="text-center mb-3">{{ question.question_text }}</h1>

{% if error_message %}
<p class="alert alert-danger">
    <strong>{{ error_message}}</strong>
</p>
{%endif%}

<form action="{% url 'polls:vote' question.id %}" method="POST">
    {%csrf_token%}
    {% for choice in question.choice_set.all %}
        <div class="form-check">
            <input type="radio" name="choice" class="form-check-input" id="choice{{ forloop.counter}}" value="{{choice.id}}"/>
            <label for="choice{{ forloop.counter}}">{{choice.choice_text}}</label>
        </div>
    {% endfor %}
    <input type="submit" value="投票" class="btn btn-success btn-lg btn-block mt-4" />
</form>
{% endblock %}

これでこの表示となります。

まず、戻るボタンを定義してるのは、「 <a class=”btn btn-secondary btn-sm mb-3″ href=”{% url ‘polls:index’ %}”>戻る</a> 」の部分となります。リンク先URLを前画面であるpollsアプリのindexとしていています。

次に「 {% if error_message %} 」の部分は、テンプレートが呼び出されたときにエラーメッセージがあるときの対応です。

<form>タグでは選択肢を書いていきます。1点、formタグで囲んだ部分には、 {%csrf_token%} をいれることを忘れないようにしてください。これはセッションハイジャックされないようにする、セキュリティ上必要な記述になります。

あとはfor文で選択肢を取り出して、inputタイプをradioにして表示しています。「 {% for choice in question.choice_set.all %} 」のchoice_set.allの部分は、従属関係のあるモデルにおいて自動で作成されるメソッドであるchoice_set.all()のことです。djangoの中では()が不要となる、という文法です。これで選択肢を一つ一つ取り出しています。

idの「 id=”choice{{ forloop.counter}}” 」のところは、djangoのテンプレートの中で使える特殊なキーワードです。forloopには、counterという属性があって、forループの中の何番目であるか、が格納されています。

スポンサーリンク

まとめ

今回は、ボタンを設置してそのボタンのリンクを動的につくりました。整理するとやっていることは簡単なのですが、初めて目にすると難解に感じますよね。僕もまだ消化しきれていないです。しっかり復習しましょう。

▶ djangoの簡単なまとめ記事もあります。

コメント

タイトルとURLをコピーしました