Django入門: フォームからPOSTする ~ 簡単な一行掲示板アプリを作る その14【Windows10】

91, 2020-10-26

目次

はじめに

この記事は「Djangoで一行掲示板を作ろう」という趣旨のシリーズの記事です。

前回はDjangoのフォームを作って配置しました。

今回はフォームから実際にPOSTリクエストを送信し、そのリクエストを処理したいと思います。

前提として開発環境のOSはWindows10, シェルはコマンドプロンプトです。
仮想環境を使っていますが、仮想環境については第一回の記事をご覧ください。
初期作業フォルダはプロジェクト内のmanage.pyのあるフォルダです。

はじめに

前回までに配置したフォームに投稿内容を入力し、送信ボタンを押すと、フォームから/bbs/に対してPOSTリクエストを送信できます。
/bbs/bbs_homeという名前が付けられ、bbs\views.py内のhome_view()に対応付けられています。
ブラウザはPOSTリクエストをWebサーバーに送信するときに一緒にフォームに入力された投稿内容も送信します。
home_view()POSTリクエストを処理し、この投稿内容から新しいPostモデルのオブジェクトを作成すれば、POSTリクエストに応えてPostモデルのオブジェクトを作るという処理が書けます。

これらの一連の処理は動的Webサイトの基礎的な処理と言えます。
POSTリクエストをビューで処理するときは、ユーザーからの入力内容が不正かどうか検証(バリデート)し、不正であれば処理を継続せずエラーとしてユーザーに表示するという処理も必要になります。
エラー処理というのは「投稿に失敗しました」とか「入力内容が不正です」とかのメッセージの出力ですね。

このPOSTの処理を書けるようになればDjangoによる動的Webサイトの制作はほぼほぼOKです。
つまり、これを覚えれば色々なアプリを作れるようになるということです。

POSTリクエストをビューで処理する

manage.pyのあるフォルダからエディタでbbs\views.pyを開きhome_view_get()home_view_post()関数を次のように編集します。

from django.shortcuts import render, redirect  # <- redirectを追加
...

def home_view_get(request, form=None):
    """
    パス bbs/ の GET
    """
    context = {}  # コンテキストを作成

    context['title'] = '一行掲示板'  # ページのタイトル
    context['posts'] = Post.objects.all()  # Postのリストを取得

    if form:
        context['form'] = form
    else:
        context['form'] = PostForm()  # フォームを保存

    # renderにコンテキストを渡しテンプレートを描画
    return render(request, 'bbs/home.html', context)


def home_view_post(request):
    """
    パス bbs/ の POST
    """
    form = PostForm(request.POST)
    if not form.is_valid():
        return home_view_get(request, form=form)

    form.save()
    return redirect('bbs_home')

home_view_get()GETリクエスト、home_view_post()POSTリクエストを処理する関数です。
home_view_get()も編集してますが、これはhome_view_post()から再利用するためです。

まず↓の部分ですが、

from django.shortcuts import render, redirect  # <- redirectを追加

django.shortcutsモジュールからredirect関数を新しくインポートしています。
redirect関数はページから別のページへリダイレクト(遷移)させる関数です。

次にhome_view_get()の↓の部分ですが、

def home_view_get(request, form=None):
    ...

home_view_get()の引数にformを加えています。デフォルト値はNoneです。
それから↓の部分ですが、

    if form:
        context['form'] = form
    else:
        context['form'] = PostForm()  # フォームを保存

home_view_get()form引数がNoneじゃなければその引数をコンテキストに、NoneであればPostForm()で新規作成したオブジェクトを代入しています。
このように処理を書くことで引数formが指定されている場合に生成するフォームを変更することが可能です。

次にhome_view_post()です。

def home_view_post(request):
    """
    パス bbs/ の POST
    """
    form = PostForm(request.POST)
    if not form.is_valid():
        return home_view_get(request, form=form)

    form.save()
    return redirect('bbs_home')

まず↓の部分ですが、

    form = PostForm(request.POST)

request.POSTPOSTリクエストのデータが入っているdjango.http.request.QueryDictです。
これはPythonの辞書(dict)のようなオブジェクトです。
このQueryDictPostForm()の引数に渡すと、その内容からフォームを構築します。
つまり↑のrequest.POSTにはユーザーの投稿した投稿内容(content)が入っているわけですが、それをフォームに渡すことでフォームは内部のcontentrequest.POSTcontentを持つようになります。
言ってしまうとただのデータのコピーです。

次の↓のif文です。

    if not form.is_valid():
        return home_view_get(request, form=form)

PostFormis_valid()というメソッドを持っています。
これはフォーム内のデータが不正かどうか判断するメソッドです。
is_valid()を呼び出すとPostFormは内部でバリデーション、つまりデータの検証を行います。
バリデーションの結果が不正であればis_valid()Falseを返し、不正でなければTrueを返します。
つまり↑のif文は「フォームのデータが不正であれば~」というif文なわけです。

フォームの内容が不正であれば↑のif文は検証に使ったフォームをhome_view_get()に渡して呼び出します。
is_valid()を呼び出したフォームは内部にエラー情報を持つようになります。
このエラー情報はユーザーに出力することが可能です。
ですのでis_valid()を呼び出したフォームはコンテキストに保存するようにします。
こうすることでテンプレートからフォームのエラー情報を参照できるようになります。

home_view_post()からhome_view_get()を呼び出すのはトンチンカンな気もしますが、こうすることで重複した処理を共通化するようにしています。
今回作っている掲示板は、すべて/bbs/GETPOSTリクエストを処理していますが、POSTリクエストを/bbs/new//bbs/create/などのパス(とビュー)に指定することも一般的です。
アプリの規模によってこの辺の設計は考える必要がありますが、今回はごくごく小規模なのでこのような設計にしています。

次に↓の部分です。

    form.save()
    return redirect('bbs_home')

PostFormforms.ModelFormを継承したクラスですが、ModelFormを継承したフォームはsave()メソッドを呼び出すことで内部データを利用してモデルのオブジェクトを作成することが出来ます。
↑のform.save()の返り値はbbs.models.Post, つまり今回作成した掲示板の投稿用モデルです。
しかしこの処理では返り値は使わないので保存していません。

最後にredirect('bbs_home')を呼び出してbbs_homeにページをリダイレクトしています。
redirect()関数の第1引数にはパスかルート名を渡します。
is_valid()を使ったif文ではhome_view_get()に処理を委譲していましたが、ここではリダイレクトです。
これはブラウザのリロードによるフォームからの2重送信を防ぐための処置です。
ためしにredirect()home_view_get()に書き換えて、フォームから投稿した後にページをリロードしてみてください。どういうことかわかると思います。

動作確認

これでPOSTリクエストの処理は完了です。
開発用サーバーを起動します。

> python manage.py runserver 0.0.0.0:8123

エラーがなければ↓のように掲示板へ投稿できるようになっているはずです。

【0091】ss00.png

おわりに

今回までで掲示板としての形は一応整いました。
不格好ですがちゃんとした動的Webサイトです。

おめでとうございます

次回から掲示板に微修正を加えていきます。

おたのしみに