DjangoのFormの値の取得方法: is_validとcleaned_data

310, 2021-08-22

目次

DjangoのFormの値の取得方法

DjangoではHTMLで書かれるフォームをFormオブジェクトに抽象化して扱うことが出来ます。
Formオブジェクトにした場合、フォームのデータのやり取りはこのFormオブジェクトを通して行われます。

この記事ではFormから、フォームに入力された値(情報)を取得する方法を詳しく解説します。
結論から言うとFormから値を取得するにはFormの属性cleaned_dataを参照します。

    title = form.cleaned_data['title']

cleaned_dataの参照ではFormのis_validメソッドの知識も合わせて必要になりますので、この記事で解説します。
具体的には↓について見ていきます。

  • Formのデータを取得するおおまかな流れ

  • 前提とするプロジェクトとアプリ

  • 前提とするForm

  • 前提とする関連モジュール

  • POSTデータをFormにセットする

  • cleaned_dataでFormからデータを取得する

関連記事
Djangoのフォーム(forms.Form)の使い方【Python】
Djangoのformの今どきな作り方【Python, モデルフォーム】
DjangoのFormの初期値の設定方法: initial属性の使い方

参考
フォームを使う | Django ドキュメント | Django
フォームとフィールドの検証 | Django ドキュメント | Django

Formのデータを取得するおおまかな流れ

Formからデータを取得する大まかな流れについて解説します。
データの取得の流れはだいたい↓のようなものです。

  • ビューでGETを処理する

  • GETに応じたフォームを描画する

  • ユーザーがフォームに入力しPOSTで送信する

  • ビューでPOSTを処理する

  • POSTのデータをフォームに渡してバリデーションをする

  • バリデーション済みのデータをフォームから取得する

というのがFormのデータを取得する大まかな流れになります。
つまりFormに格納されるデータというのはPOSTのデータなんですね。
そしてそのPOSTのデータがフォームでバリデーションされて、そのバリデーションされたデータをcleaned_dataで取得すると言う感じになります。

今回の解説ではGETでフォームを描画する所から解説します。

前提とするプロジェクトとアプリ

前提として、解説用に作成しているDjangoのプロジェクトはmysiteという名前になります。
それからmyappというアプリを作成済みです。

前提とするForm

まず前提とするFormですが、今回は本をテーマにフォームを定義したいと思います。
本のタイトルと説明を格納するフォームBookFormを作ります。
このBookFormmyapp/forms.pyに保存しておきます。

# myapp/forms.py
from django import forms


class BookForm(forms.Form):
    title = forms.CharField(
        label='本のタイトル',
        max_length=100,
        required=True,
        help_text='必須',
    )
    description = forms.CharField(
        label='本の説明文',
        widget=forms.Textarea,
        required=False,
        help_text='*任意',
    )

BookFormtitledescriptionの2つの属性を持つシンプルなフォームです。

前提とする関連モジュール

関連モジュールとしてまずmysite/urls.pyは↓のようになっています。

# mysite/urls.py
from django.contrib import admin
from django.urls import path
from myapp.views import home

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', home, name='myapp_home'),
]

今回は手抜きでアプリではなくmysite(プロジェクト)のほうのurls.pyにルート情報を書いています。
myapp.viewshomeビューはインデックス用のビューになります。

それからmyapp/views.pyは↓のように定義しています。

# myapp/views.py
from django.shortcuts import render
from django.http import HttpResponse
from myapp.forms import BookForm


def home(request):
    # GETメソッドを処理する
    if request.method == 'GET':
        form = BookForm()  # フォームを作成して
        context = { 'form': form }  # コンテキストに保存して
        return render(request, 'myapp/home.html', context)  # 描画

    # POSTメソッドを処理する
    elif request.method == 'POST':
        form = BookForm(request.POST)  # POSTデータをフォームに保存する
        if not form.is_valid():  # バリデーションを行う
            # データが不正だったらフォームを再描画する
            context = { 'form': form }
            return render(request, 'myapp/home.html', context)

        # ↓がフォームから値を取得している所
        title = form.cleaned_data['title']
        description = form.cleaned_data['description']

        # 値を元にレスポンスを生成する
        return HttpResponse(f'{title}: {description}')

    # 未対応のメソッド
    else:
        return HttpResponse('不正なメソッドです', status=500)

このmyapp/views.pyが今回の解説の本丸となるところです。
これについて後述で詳しく見ていきます。

それからテンプレートファイルmyapp/templates/myapp/home.htmlは↓のようになっています。

{# myapp/templates/myapp/home.html #}

<h1>本の情報登録フォーム</h1>

<form action="{% url 'myapp_home' %}" method='POST'>
  {% csrf_token %}
  {{ form.as_p }}
  <input type="submit" value="登録" />
</form>

こちらのテンプレートファイルではフォームを描画しています。
フォームのアクションは先ほどのhomeビューへのルートを指定します。
methodPOSTです。

これまでのビューとテンプレートを描画すると↓のような画面になります。

home.png

POSTデータをFormにセットする

肝心のmyapp/views.pyについて詳しく見ていきます。
まずhomeビュー内ではメソッドを処理して分岐しています。

def home(request):
    # GETメソッドを処理する
    if request.method == 'GET':
        ...

    # POSTメソッドを処理する
    elif request.method == 'POST':
        ....

    # 未対応のメソッド
    else:
        ....

request.methodGETの場合とPOSTの場合とで処理を分岐しています。
POSTメソッド内の処理を見てみます。

    # POSTメソッドを処理する
    elif request.method == 'POST':
        form = BookForm(request.POST)  # POSTデータをフォームに保存する
        if not form.is_valid():  # バリデーションを行う
            # データが不正だったらフォームを再描画する
            context = { 'form': form }
            return render(request, 'myapp/home.html', context)

        # ↓がフォームから値を取得している所
        title = form.cleaned_data['title']
        description = form.cleaned_data['description']

        # 値を元にレスポンスを生成する
        return HttpResponse(f'{title}: {description}')

POSTメソッドの場合、request.POSTに送信されたデータが格納されています。
request.POSTPOSTは辞書に似たデータ構造です。
この辞書をBookFormのコンストラクタ(初期化関数)に渡します。

        form = BookForm(request.POST)  # POSTデータをフォームに保存する

こうすることでrequest.POSTのデータを持ったフォームを作ることが可能です。
あとはこのフォームのis_valid()メソッドを呼び出して、フォーム内のデータをバリデーションさせます。
is_valid()Trueであればフォーム内のデータは正常で、Falseであれば異常になります。

is_validは内部ではerrors属性を参照しています。
errors属性はプロパティで、このプロパティを参照したときにフォームのfull_clean()メソッドが実行されます。
full_clean()は各種フィールドやフォームのバリデーションを一括で行っています。

is_valid()Falseの場合は適当にHttpResponseを生成してユーザーに不正なデータであることを通知します。

        if not form.is_valid():  # バリデーションを行う
            # データが不正だったらフォームを再描画する
            context = { 'form': form }
            return render(request, 'myapp/home.html', context)

cleaned_dataでFormからデータを取得する

フォームのデータが正常であればフォームの属性cleaned_dataを参照してフォーム内に保存されているデータを取得します。
cleaned_dataは辞書のようなオブジェクトで、辞書のように扱うことが出来ます。

        # ↓がフォームから値を取得している所
        title = form.cleaned_data['title']
        description = form.cleaned_data['description']

あとは適当にフォームの値を使ってレスポンスを生成します。

        # 値を元にレスポンスを生成する
        return HttpResponse(f'{title}: {description}')

今回はレスポンスの生成はHttpResponseを使って手抜きしていますが、もちろんrender()などを使ってもOKです。

おわりに

今回はDjangoのFormの値の取得方法を見てみました。
フォームを使えばバリデーションなども簡単に行えますし、値の取得も手軽に行えます。
Djangoのフォームに慣れてしまえば色々手っ取り早くコードを書くことも出来るので、至れり尽くせりと言う感じですね。

笑顔をフォームする

いい笑顔だ