pyhaya’s diary

機械学習系の記事をメインで書きます

Djangoのチュートリアル 6 フォームを書く

今回は、Djangoで作ったページにフォームを追加していきます。前回までの記事はこちらです。

Djangoのチュートリアル 1 インストールから最初のビュー作成まで - pyhaya’s diary
Djangoのチュートリアル 2 モデルを作成する - pyhaya’s diary
Djangoのチュートリアル 3 ビューとモデルを組み合わせる - pyhaya’s diary
Djangoのチュートリアル 4 HTMLに慣れる - pyhaya’s diary
pyhaya.hatenablog.com

目次

フォームとは

Webページにおけるフォームというのは、Webページにおいてユーザーが入力を行う部分になります。通常はformタグを使って、その中にinputタグなどを使って入力部を作っていきます。例えばお問い合わせフォームなどがあります。詳しくは下のページを見てください。
html-coding.co.jp

フォームの例

フォームに慣れるためにいくつかフォームを作ってみます。htmlタグなどは省略して、body内のみを書いていきます。

<form method="POST" action="">
    <p>名前<input type="text"></p>    <!-- テキスト入力部 -->
    <p>身分
        <input type="radio">会社員        <!-- ラジオボタン -->
        <input type="radio">学生
        <input type="radio">主婦
        <input type="radio">無職
    </p>
    <p>年齢:
        <select>        <!-- 選択 -->
            <option>10歳以下</option>
            <option>10~20歳</option>
            <option>20~30歳</option>
            <option> それ以上</option>
        </select>
    </p>
    ご要望:
    <p>
        <textarea rows=5 cols=30>    <!-- テキストエリア -->
        </textarea>
    </p>
    <p><input type="submit" value="送信"></p>    <!-- 送信ボタン -->
</form>

いろいろ書きましたが、これをブラウザで見ると下のようになります。
f:id:pyhaya:20181101075854p:plain

フォームを作る

ではDjangoを使ってフォームを実装してみます。

どこを変更するか

今回はdetail.htmlを変更していきます。前回までで作ったdetail.htmlは下のようでした。

<!DOCTYPE html>
<html>
    <head>
        <meta charset='utf-8'>
    </head>
    <body>
        <h1>{{ question.question_text }} </h1>
        {% for choice in question.choice_set.all %}
        <li>{{ choice.choice_text }}</li>
        {% endfor %}
    </body>
</html>

これを下のように変更します。

<!DOCTYPE html>
<html>
    <head>
        <meta charset='utf-8'>
    </head>
    <body>
        <h1>{{ question.question_text }} </h1>
        <form action="{% url 'polls:vote' question.id %}" method="post">
            {% csrf_token %}
            {% for choice in question.choice_set.all %}
            <p>
                <input type="radio" name="choice" id="choice{{ forloop.counter }}"
                                                  value="{{ choice.id }}">
                <label for="choice{{ forloop.counter }}">
                    {{ choice.choice_text }}
                </label>
            {% endfor %}
            </p>
            <p>
            <input type="submit" value="Vote">
            </p>
        </form>
    </body>
</html>

だいぶいろいろ付け加わりました。

formタグの書き方

8行目を見てみると、ここでフォームの定義とアクションが設定されています。アクションには前回の記事で説明したように、{% url %}タグを用いています。しかしその中身は前回説明したものとは少し異なっています。'polls:vote'とは何でしょうか?これはpollsがアプリケーションの名前で、voteがname=によって識別された名前を表しています。pollsアプリケーションを認識させるためにはもうひと手間必要ですが、これはurls.pyを編集するところで解説します。

もうひとつ重要なのが、methodの欄です。データベースに変更があるときには必ず"post"にする必要があります

forループ

HTML内でのforループが再び出てきました。もう見慣れてきたかもしれませんが、こちらも今までとは異なる要素が入ってきています。それはforloop.countです。これはforループが何回回ったかをあらわすものです。

ビューを編集する

detail.htmlを書き換えましたので、ビューもこれを正しく表示できるように変更しましょう。detail.htmlにはVoteボタンがあるので、これが押されたときの遷移画面も考えなくてはいけません。

views.py

def index(request):
    latest_question_list = Question.objects.order_by('-pub_date')[:5]
    context = {'latest_question_list' : latest_question_list}
    return render(request, 'polls/index.html', context)

def detail(request, question_id):
    question = Question.objects.get(pk=question_id)
    return render(request, 'polls/detail.html', {'question' : 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):
        return render(request, 'polls/detail.html', {
            'question' : question,
            'error_message' : 'You did not select a choice',
            })

    else:
        selected_choice.votes += 1
        selected_choice.save()
        return HttpResponseRedirect(reverse('polls:results', \
            args=(question.id,)))

def results(request, question_id):
    question = get_object_or_404(Question, pk=question_id)
    return render(request, 'polls/results.html', {'question' : question})

vote関数について説明をします。

get_object_or_404(Question, pk=question_id)

これは、Questionオブジェクトの中からpk要素がquestion_idに等しいものがある場合にはそれを取得し、ない場合には「404 ページが見つかりませんでした」の表示を行います。

HttpResponseRedirect(reverse('polls:results', args=(question.id,)))

reverse関数というものが登場しています。これは{% url %}タグと似ていて、URLのハードコードを避けることができます。'polls:result'を探し出して引数としてquestion_idを渡します。

Voteボタンが押されたらHttpResponseRedirectが呼ばれてresultsに結び付けられているページへ遷移する仕組みになっています。

resultsビューはすぐ下に定義されていて、results.htmlに結び付けられています。ではこのページについても見ておきましょう。

results.htmlの中身

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
    </head>
    <body>
        <h1>{{ question.question_text }}</h1>
        <ul>
        {% for choice in question.choice_set.all %}
        <li>{{ choice.choice_text }}--{{ choice.votes }} vote</li>
        {% endfor %}
        </ul>
        <a href="{% url 'polls:detail' question.id %}">Vote Again?</a>
    </body>
</html>

ここに関しては新しい要素というのは出てきてはいません。Vote Againにリンクが結び付けられていて、質問ページに戻れるようになっています。

urls.pyを編集する

ではこれまでの編集で新たに必要になった要素をurls.pyに付け足していきます。

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'),
        ]

urlpatternだけではなく、app_nameという要素が付け加わったことに注目してください。HTMLファイルを編集していた時に'polls:results'のような表現が現れていたことを覚えているでしょうか?この'polls'がapp_nameに対応しています。

お疲れ様です。これで完成です。今回の記事では、実際にpollsアプリケーションで投票操作を実装しました。