【Django】FormViewの使い方(クラスベースビューで汎用的なフォームを扱う)

FormViewでのクラスベースビューの実現方法解説ページアイキャッチ

このページにはプロモーションが含まれています

このページでは、Django フレームワークで定義される FormView の使い方について説明していきます。

この FormViewView というクラスのサブクラスの1つであり、ビューをクラスベースで作成する際に利用するクラスとなります。この View のサブクラス を継承し、さらにクラス変数を定義したりメソッドをオーバーライドすることで、あなたが開発したいアプリに応じたビューを作成することが可能となります。

この辺りのクラスベースビューやクラスベースビューの作り方については下記ページで解説していますので、詳しくはこちらをご参照ください。

クラスベースビューの解説ページアイキャッチ【Django入門14】クラスベースビューの基本

今回は、多数存在する View のサブクラス の中から FormView に焦点を当てて解説を行なっていきます。

FormView

では、FormView について解説していきたいと思います。

FormView は、フォームを扱うページを実現する View のサブクラス となります。

FormViewdjango.views.generic から import して利用します。

FormViewのimport
from django.views.generic import FormView

FormView は基本的にデータベース操作なし

実は、Django フレームワークにはフォームを扱うページを実現する View のサブクラス が他にも用意されています。代表的なフォームを扱う View のサブクラス としては、CreateViewUpdateView が挙げられます。これらに関しては下記のページで詳細を解説していますので、詳しくは下記ページを参照していただければと思います。

CreateViewでのクラスベースビューの実現方法解説ページアイキャッチ【Django】CreateViewの使い方(クラスベースビューでの新規登録ページの実現) UpdateViewでのクラスベースビューの実現方法解説ページアイキャッチ【Django】UpdateViewの使い方(クラスベースビューでのレコード更新ページの実現)

これらの CreateViewUpdateViewFormView との決定的な違いは、データベース操作を行うことを前提として定義されているかどうかという点にあります。

CreateView はフォームを扱い、ユーザーがフォームから送信してきたデータからインスタンスを生成し、それをレコードとしてデータベースの特定のテーブルに新規登録することを前提とした View のサブクラス となります。また、UpdateView は、既にデータベースの特定のテーブルに存在しているレコードをユーザーがフォームから送信してきたデータから生成したインスタンス(レコード)で上書き・更新することを前提とした View のサブクラス となります。

このように、これらの View のサブクラス ではフォームから送信されてきたデータに基づいてデータベースの操作を行うことを前提としたクラスとなっています。そもそも、フォームとデータベースのレコードは密接に関連しており、ウェブアプリではデータベースの操作をユーザーからの入力に基づいて行うためにフォームを扱うことが多いです。例えば、コメントを投稿するようなウェブアプリでは、コメントの本文等を入力するフィールドを備えたフォームを表示し、そのフィールドに入力されたデータから生成したインスタンスをレコードとしてコメント格納用のテーブルに保存が行われることになります。

CreateViewが送信されてきたデータに基づいてデータベース操作を行う様子

また、Django において、データベースのテーブルはモデルクラスによって表現され、さらにテーブルのレコードは、そのモデルクラスのインスタンスとして扱われます。さらに、そもそもフォームはデータベースの特定のテーブルに対して操作を行うことを目的として利用する機会が多いため、Django にはフォームをモデルクラスに基づいて自動的に生成するような仕組みも存在しています。それが、下記ページでも紹介しているモデルフォームクラスになります。そして、前述で紹介した CreateViewUpdateView は、フォームとしてモデルフォームクラスを扱うことを前提としたものとなっています。

Djangoのモデルフォーム解説ページアイキャッチ【Django入門8】モデルフォームの基本

こういった前提のもとに定義されている View のサブクラス であるため、CreateViewUpdateView を利用することで、簡単にデータベースの特定のテーブルに対してレコードの新規登録やレコードの更新を行うようなビューを簡単に実現することが可能となります。

しかし、機会が多いだけで、フォームを扱うからといって必ずしもデータベースの操作が行われるとは限りません。例えば、2つの数字の入力を受け付けるフォームを表示し、ユーザーから入力された2つの数字の掛け算結果を表示するようなビューの場合、わざわざ入力された数字をデータベースに保存する必要はなく、単にフォームから送信されてきた2つの数字の掛け算結果をページに表示してやれば良いだけになります。

データベース操作を行わずにレスポンスを返却する様子

このように、フォームは扱うもののデータベースの操作を伴わないビューを実現する場合、データベースの操作を行うことを前提として定義されている CreateViewUpdateView を利用するのでは、むしろ実現が難しくなります。

なので、データベースの操作を行うことを前提としていない View のサブクラス を利用した方が良いです。そして、そのデータベースの操作を行うことを前提としていないフォームを扱うView のサブクラス が、このページで主題となっている FormView となります。

FormViewが送信されてきたデータに基づいたデータベース操作を行わ図にレスポンスを返却する様子

このように、FormView はフォームは扱うもののデータベース操作を伴わないビューを実現するのに適した View のサブクラス となります。

スポンサーリンク

送信されてきたデータの扱いはカスタマイズ次第

さて、前述の通り、CreateView においては、フォームからのデータの送信があった際に送信されてきたデータからインスタンスを生成し、それをレコードとしたデータベースへの新規登録が行われるようになっています。また、UpdateView においては、既にデータベースに登録済みのレコードを、送信されてきたデータに基づいて生成したレコード(インスタンス)で上書きする処理が行われるようになっています。ここで重要なポイントは、フォームから送信されてきたデータの扱いはクラスによって決まっているという点になります。

送信されてきたデータの扱いがクラスによって決まっている様子

また、これらのクラスでは、上記の処理が完了した後に特定の URL へのリダイレクトレスポンスの返却が行われるようになっています。

では、FormView ではフォームからのデータの送信があった際にどんな処理が行われるのでしょうか?

結論を言うと、特定の URL へのリダイレクトレスポンスの返却のみが行われます。つまり、フォームから送信されてきたデータは無視されます。これだとフォームを扱う意味がありませんね…。こんな View のサブクラス がなぜ用意されているのでしょうか…。

この理由も単純で、FormView は開発者からカスタマイズされることを前提とした作りとなっているからになります。もちろん、他の View のサブクラス もカスタマイズされることを前提とした作りとなっているのですが、FormView の場合はカスタマイズが必須で、特に form_valid というメソッドのオーバーライドが必須となっています。

そして、このメソッドをオーバーライドして内部の処理を実装してやることで、開発者が自由にフォームから送信されてきたデータの扱いを決めることができます。具体的なオーバーライドの手順等は後述で解説していきますが、FormView 自体はフォームを扱う View のサブクラス であるもののフォームから送信されてきたデータは基本的に無視されるようになっていること、さらにはカスタマイズ次第でフォームから送信されてきたデータの扱いを開発者自身が自由に決めることができることは覚えておくと良いと思います。

DeleteViewの場合、送信されてきたデータの扱いがカスタマイズ次第であることを示す図

補足しておくと、CreateViewUpdateView でもカスタマイズによって送信されてきたデータの扱いを変更させることは可能です。例えば CreateView でも、フォームから送信されてきたデータのレコードとしてのデータベースへの新規登録を行わないようにすることも可能です。

ただ、送信されてきたデータの扱いを変更してしまうと CreateViewUpdateView の役割が曖昧になってしまいますし、これらをわざわざ利用する意味も無くなってしまうことになるため、ここに関しては変更せずに利用するケースの方が多いと思います。重要なのは、各 View のサブクラス の役割を理解し、それに応じて使い分けることです。

FormView でのクラスベースビューの作り方

では、次は FormView でクラスベースビューを作成する手順について説明していきます。

FormView でのクラスベースビューの作り方に関しては、ここまで紹介してきた CreateViewUpdateView の場合とほぼ同様となります。

要は、views.py に FormView を継承するクラス(サブクラス)を定義し、views.py に定義したクラスにクラス変数やメソッドを定義することで FormView で定義されているクラス変数の上書きメソッドのオーバーライドを行なっていきます。これにより、FormView の特徴を活かしながら自身のウェブアプリに応じたビューにカスタマイズしていくことが可能となります。

クラスベースビューの作り方の説明図

また、これも CreateViewUpdateView と同様に、FormView ではリクエストに応じて get メソッドや  post メソッドが実行されるようになっており、FormView のクラス変数や他のメソッドは、これらの get メソッドおよび post メソッドから利用されるようになっています。したがって、FormView のサブクラス側でクラス変数やメソッドを上書き・オーバーライドしてやれば、この get メソッドと post メソッドの動作を変化させることができます。

FormViewのgetメソッドやpostメソッドからクラス変数や他のメソッドが利用される様子

FormView のサブクラスを定義する上で基本的に必須になるのは下記の3つとなります。

  • 利用するフォームクラスを指定する
  • 利用するテンプレートファイルを指定する
  • form_valid のオーバーライドを行う

利用するフォームクラスを指定する

まず、FormView はフォームを扱うために利用する View のサブクラス であり、get メソッドではフォームを表示するために、また post メソッドではフォームから送信されてきたデータを扱うためにフォームクラスのインスタンスが利用されることになります。そして、そのフォームクラスを指定するのがクラス変数 form_class の役割となります。

そのため、FormView のサブクラスを定義する際にはクラス変数 form_class を定義しておく必要があります。

また、form_class には forms.py 等で定義されているフォームクラスを指定しておく必要があり、クラス変数 form_class を定義していなかった場合、get メソッドや post メソッドが実行された際に下記のような例外が発生することになります。

'NoneType' object is not callable

利用するフォームを指定する必要があるのは CreateViewUpdateView の場合でも同様なのですが、これらの場合は form_class 以外のクラス変数の定義でもクラスで扱うフォームを指定することが可能でした(modelfields)。それに対し、FormView の場合はフォームクラスを指定するクラス変数は form_class の一択となります。

また、CreateViewUpdateView の場合は form_class にはモデルフォームクラス(forms.ModelForm を継承するクラス)を指定する必要がありましたが、FormView の場合はモデルフォームクラスでも単なるフォームクラス(forms.Form を継承するクラス)でも問題ありません。

スポンサーリンク

利用するテンプレートファイルを指定する

また、FormView のサブクラスではクラス変数 template_name の定義も必須となります。この template_name には FormView のサブクラスが利用するテンプレートファイルのパスを指定します。

CreateViewUpdateView の場合、template_name を定義しなかった場合はクラスによって設定されているデフォルトのパスのテンプレートファイルが利用されることになります。なので、template_name を定義しなくてもフォームの表示等を行うことは可能でした。それに対し、FormView のサブクラスにはデフォルトのテンプレートファイルのパスが存在しないため、テンプレートファイルを利用するためには必ずクラス変数として template_name を定義しておく必要があります。

template_name を定義しなかった場合は get メソッドが実行される際に例外が発生することになります。

form_valid のオーバーライドを行う

さらに、送信されてきたデータの扱いはカスタマイズ次第 で説明したように、FormView に関してはフォームから送信されてきたデータを扱う処理を開発者自身が実装する必要があります。これを行わないとフォームからデータが送信されてきても無視されてしまうことになります。

そして、このフォームから送信されてきたデータを扱う処理は FormView のサブクラスの form_valid メソッドに実装することになります。この form_valid メソッドは FormView で定義されていますので、要は form_valid メソッドのオーバーライドを行ないます。

この form_valid メソッドは FormView だけでなく、CreateViewUpdateView などのフォームを扱う View のサブクラス 全般に用意されているメソッドです。そして、このメソッドはフォームから送信されてきたデータの妥当性の検証結果が OK の場合の処理を実装するメソッドとなります。

CreateViewUpdateView の場合、これらのビューで実現することは明白で、CreateView の場合は、それが「フォームから送信されてきたデータをレコード(インスタンス)としてデータベースに新規登録すること」になります。そのため、CreateViewform_valid メソッドではレコードのデータベースへの新規登録処理が記述されています。そして、その後に特定の URL に対するリダイレクトレスポンスを返却する処理が記述されています。

CreateViewのform_validに記述されている処理

UpdateView の場合もほぼ同様ですが、UpdateViewform_valid メソッドではレコードの新規登録処理ではなく、データベースに登録済みのレコードの更新処理が記述されています。

これらに対し、FormView の form_valid メソッドには特定の URL に対するリダイレクトレスポンスを返却する処理しか記述されていません。そのため、前述の通り FormView ではフォームからデータが送信されてきたとしても、そのデータに対する処理は一切行わないようになっています。正直これだとユーザーに対してフォームを表示してデータの入力受付を行う意味がないですね…。

FormViewのform_validに記述されている処理

つまり、FormView のサブクラスで実現されるビューは、form_valid をオーバーライドしない限りフォームを扱う意味のないビューしか実現できないことになります。

そのため、FormView はサブクラス側で form_valid メソッドを開発者自身が定義してオーバーライドすることを前提とした View のサブクラス と考えて良いと思います。そもそも、FormView の場合、CreateViewUpdateView のようにビューで実現することが明白ではなく、実現することが曖昧な View のサブクラス となっています。違う言い方をすれば、FormView は単にフォームを扱うための View のサブクラス であり、そのフォームの利用目的は開発するウェブアプリによって異なります。なので、”開発するウェブアプリに応じて自由に form_valid メソッドを開発者が定義してください” というスタンスの View のサブクラス となっているのだと思います。

FormViewのサブクラス側でform_validを定義してオーバーライドする様子

そのため、FormView の場合、開発者自身が form_valid メソッドを定義し、ウェブアプリの動作に合わせた処理を実装する必要があります。もちろん、特定の URL に対してリダイレクトを行うだけで良いのであれば form_valid メソッドを定義してオーバーライドする必要もないのですが、それだとフォームによってユーザーからのデータの送信を受け付ける意味もありませんので、基本は form_valid メソッドを定義してオーバーライドすることは必須であると考えて良いです。

FormView のクラス変数

では、次は FormView で定義されているクラス変数の紹介を行なっていきます。これらのクラス変数を FormView を継承するクラスで定義し直して上書きすることでクラスの動作のカスタマイズを行うことが可能となります。

FormView の場合、GET メソッドのリクエストを受け取った際に get メソッドが実行され、GET メソッドのリクエストを受け取った際に post メソッドが実行されることになるため、これらの get メソッドや post メソッドの動作を変化させることを目的にクラス変数の定義を行なっていくことになります。

スポンサーリンク

FormView のクラス変数の一覧

この FormView で定義されるクラス変数には下記のようなものが存在します。

各リンクをクリックすれば括弧内に示した View のサブクラス の解説ページにジャンプするようにしています。基本的に、フォームを扱うための設定を行うクラス変数に関しては CreateView の解説ページ、その他のクラス変数に関しては ListView の解説ページにジャンプするようにしています。

FormView でのクラスベースビューの作り方 でも説明したように、FormView ではクラス変数 form_classtemplate_name の定義が基本的に必須になるので注意してください。POST リクエストを受け取り、さらにフォームから送信されてきたデータの妥当性の検証結果が OK の場合にリダイレクトを行うようにしたい場合は success_url の定義も必須となります。

また、CreateViewUpdateView、さらには ListViewDetailView 等には共通してクラス変数 model が存在しますが、FormView にはクラス変数 model は存在しません。model は、モデルクラスを指定することで操作対象のデータベースのテーブルを各クラスに指定する役割があったのですが、FormView はデータベースの操作を行うことは前提となっていないためクラス変数 model が存在しません。同様の理由で、FormView にはデータベースに発行するクエリを指定する queryset なども存在せず、上記で挙げた View のサブクラス に比べると FormView におけるクラス変数の数は少なくなっています。

FormView のメソッド

次に FormView の持つメソッドを紹介していきます。FormView を継承したクラスを定義し、そのクラスで FormView の持つメソッドをオーバーライドしてやることで FormView とは異なる動作のクラスを実現することができるようになります。

FormView のメソッド一覧

FormView の持つメソッドの一覧は下記のようになります。あくまでもクラスのカスタマイズ目的でオーバーライドを行う可能性のあるものを挙げており、as_view などのカスタマイズは行わないであろうメソッドは省略しています。

また、FormViewget メソッドと post メソッドを持っており、メソッドが GET のリクエストを受け取った場合は get が実行され、メソッドが POST のリクエストを受け取った場合は post が実行されようになっています。そして、これらの getpost の中から各種メソッドが実行されるようになっています。

  • get:リクエストのメソッドが GET の場合の処理を実行する
  • post:リクエストのメソッドが POST の場合の処理を実行する
  • get_context_data:テンプレートに渡すコンテキストを生成する
  • get_form:フォームクラスのインスタンスを取得する
  • get_form_class:フォームクラスを取得する
  • get_form_kwargs:フォームクラスのコンストラクタに指定するパラメータを取得する
  • get_initialinitial への指定値を取得する
  • get_prefixprefix への指定値を取得する
  • form_valid:特定の URL へのリダイレクトレスポンスを返却する(妥当性の検証が OK の時に実行される)
  • form_invalid:再度フォームを表示する(妥当性の検証が NG の時に実行される)
  • get_success_url:リダイレクト先の URL を取得する
  • get_template_names:テンプレートファイルの名前を取得する
  • render_to_response:レスポンスを返却する

これも FormView でのクラスベースビューの作り方 でも説明したように、FormViewform_valid ではリダイレクトレスポンスの返却のみが行われることになるため、フォームからユーザーがデータを送信してきたとしても、そのデータは無視されることになります。つまり、FormViewform_valid をそのまま利用するとフォームを扱う意味が無いことになります。

なので、FormView のサブクラスを “フォームを扱う意味があるクラス” とするためには、この form_valid に関してはオーバーライドが必須となります。

ちなみに、送信されてきたデータの妥当性の検証結果が NG の場合は form_invalid メソッドが実行されることになり、このメソッドでは再度フォームの表示を行うための処理が行われます。この処理自体は CreateView 等の他のフォームを扱う View のサブクラス と同じであり、送信されてきたデータの妥当性の検証結果が NG の場合の典型的な処理が実現されているため、特に変更する必要はないと思います(もちろん必要に応じて変更しても良いです)。 

スポンサーリンク

form_valid のオーバーライド

ということで、ここでは form_valid のオーバーライドについて解説しておきたいと思います。

form_valid の定義

まず、form_valid のオーバーライドは、FormView のサブクラス側で form_valid メソッドを定義することで実現することができます。ただし、引数や返却値は元々の FormView の form_valid メソッドに合わせる必要があります。

この FormView の form_valid メソッドは下記のように定義されています。

FormViewのform_valid
def form_valid(self, form):
    """If the form is valid, redirect to the supplied URL."""
    return HttpResponseRedirect(self.get_success_url())

ご覧の通り、引数として selfform を受け取るようになっており、HttpResponseRedirect のインスタンスを返却するようになっています。

返却値は HttpResponseRedirect のインスタンスとなっていますが、レスポンスが返却されるのであれば違うクラスのインスタンスを返却するのでも問題ありません。ここでレスポンスとは、 HttpResponse のサブクラスのインスタンスとなります。

HttpResponseRedirectHttpResponse のサブクラスの1つであり、リダイレクトを行いたい場合は、上記を真似て HttpResponseRedirect のインスタンスを返却するので問題ありません。それ以外の場合は、目的に応じた HttpResponse のサブクラスを選択し、そのインスタンスを返却するようにしてやれば良いです。

また、HttpResponse のサブクラスのインスタンスを返却する関数やメソッドも Django フレームワークには多数用意されていますので、その実行結果を返却するのでも良いです。例えば先ほど示したメソッド一覧の中にある render_to_response に関しても HttpResponse のサブクラスのインスタンスを返却するメソッドとなっています。また、関数ベースビューの時に利用することの多い render 関数も HttpResponse のサブクラスのインスタンスを返却するメソッドとなります。

ここまでをまとめると、form_valid のオーバーライドを行う場合、まずは FormView のサブクラスに下記のような形式の form_valid を定義してやれば良いことになります。

form_validのオーバーライド
def form_valid(self, form):
    # 略
    return HttpResponseのサブクラスのインスタンス

フォームから送信されてきたデータを取得する

続いて、form_valid 内部の処理を実装していきます。

この form_valid のオーバーライドを行う目的は「フォームから送信されてきたデータを扱う」ことになります。どのように扱うかはウェブアプリによって異なりますが、フォームから送信されてきたデータを扱うためには、まずフォームから送信されてきたデータから各種フィールドのデータを取得する必要があります。

したがって、form_valid にはフォームから送信されてきたデータから各種フィールドのデータを取得する処理を記述する必要があります。具体的には、フォームから送信されてきたデータは妥当性の検証が行われた後に、引数 formcleaned_data データ属性に辞書データとしてセットされる形で form_valid に渡されることになりますので、この form.cleaned_data からデータを取得することになります。

例えば下記のように forms.py でフォームクラス BmiForm を定義した場合、

フォームクラスの定義
from django import forms

class BmiForm(forms.Form):
    height = forms.FloatField()
    weight = forms.FloatField()

下記のような処理によって、form_valid でフォームの各種フィールドに入力されたデータを取得することができます。

各種フィールドのデータの取得
def form_valid(self, form):
    height = form.cleaned_data.get('height')
    weight = form.cleaned_data.get('weight')

    # 略
    return HttpResponseのサブクラスのインスタンス

フォームから送信されてきたデータを扱う

上記のような処理によって、ユーザーがフォームの各種フィールドに入力したデータを取得することができますので、後は、これらのデータを利用して目的に応じた処理を実行してやれば良いことになります。

どんな処理を行うのかはウェブアプリによって異なりますが、取得したデータを利用して何らかの処理を行い、その結果を埋め込んだ HTML をレスポンスとして返却するケースが多いのではないかと思います。

FormViewの処理の流れの例

例えば、先ほどの BmiForm であれば、heightweight フィールドの数値を取得してやれば BMI の計算が可能となりますので、その計算を行なって結果を HTML に埋め込んでレスポンスを返却してやることで、ウェブブラウザで表示するページに BMI が出力されるようになります。そして、これは FormView のサブクラスで下記のように form_valid を定義してやることで実現することができます。

BMIの表示
def form_valid(self, form):
    height = form.cleaned_data.get('height')
    weight = form.cleaned_data.get('weight')

    bmi = weight / ((height / 100) * (height / 100))

    return self.render_to_response(self.get_context_data(form=form, bmi=bmi))

前述の通り、form_valid では HttpResponse のサブクラスのインスタンスを返却することが必要です。で、上記で return している render_to_response の返却値は HttpResponse のサブクラスのインスタンスとなりますので、このような処理を記述しておけば “HttpResponse のサブクラスのインスタンスを返却すること” を満たすことができます。

MEMO

render_to_response はクラス変数 response_class で指定したクラスのインスタンスを返却するようになっており、クラス変数 response_class を定義しなかった場合は TemplateResponse というクラスのインスタンスが返却されることになります

そして、この TemplateResponse は  HttpResponse のサブクラスの1つとなります

また、render_to_response メソッドの返却値を return した場合、この返却値を受け取った Django フレームワーク側でテンプレートファイルとコンテキストを利用して HTML が生成され、それがレスポンスとしてクライアントに返却されることになります。この時に利用されるテンプレートファイルはクラス変数 template_name で指定したパスのものとなります。

また、利用されるコンテキストは render_to_response メソッドの引数で指定されるコンテキストとなり、上記では get_context_data メソッドの実行結果がコンテキストとして利用されることになります。get_context_data はコンテキストを生成するメソッドであり、キーワード引数を指定して実行した場合、そのキーワード引数をそのまま要素として追加したコンテキストが生成されるようになります。そのため、上記のように引数に bmi=bmi を指定しておけば、キーが 'bmi' で値が BMI 算出結果となる要素がコンテキストに追加されるため、これをテンプレートファイルから参照して BMI 算出結果を HTML に埋め込むことができるようになります。

ビューとコンテキストとテンプレートファイルの関係

また、上記の様に get_context_data の引数に form=form を指定すれば、ユーザーがフォームに入力した各種フィールドのデータがセットされた状態のフォームクラスのインスタンスが 'form' キーにセットされたコンテキストが生成されることになります。それに対し、キーワード引数として form を指定しなかった場合は、各種フィールドに初期値がセットされた状態のフォームクラスのインスタンスが 'form' キーにセットされたコンテキストが生成されます。初期値が指定されていないフィールドに関しては空欄になります。

ここで補足を加えておくと、render_to_response メソッドを利用しなくても、関数ベースビューで良く利用する render 関数でも同じページの表示結果を得ることができます。で、これを FormView のメソッドを極力利用するように変更したものが、先ほど示した form_valid となります。

renderによるBMIの表示
def form_valid(self, form):
    height = form.cleaned_data.get('height')
    weight = form.cleaned_data.get('weight')

    bmi = weight / ((height / 100) * (height / 100))

    context = {
        'form': form,
        'bmi': bmi
    }

    return render(self.request, self.template_name, context)

これは form_valid のオーバーライドの一例で、実際には開発したいウェブアプリに応じた実装を行う必要があります。ただ、上記のように「FormViewform_valid と引数・返却値を合わせた form_valid を定義」し、さらに「フォームから送信されてきた各種フィールドに入力されたデータを引数 form より取得」し、「その取得したデータを扱って何らかの処理を行う」という流れが実現できるように実装することが多いと思いますので、この流れは是非覚えておいてください。

FormView が生成するコンテキスト

続いて FormView が生成するコンテキストについて説明しておきます。

FormViewget_context_data メソッドの中で生成するコンテキストには下記の要素が含まれます。この “フォームクラス” はクラス変数 form_class で指定したクラスとなります。

  • 'form' : フォームクラスのインスタンス

このように、FormView が生成するコンテキストにはフォームクラスのインスタンスしかセットされていません。

他のデータをコンテキストにセットしてテンプレートファイルから参照できるようにしたい場合は、ListView の解説ページの extra_context で説明しているクラス変数 extra_context を定義したり、get_context_data のオーバーライドを行なったりしてコンテキストの要素を追加する必要があります。

また上記のように get_context_data メソッドで 'form' キーしか持たないコンテキストが生成されるのは、get_context_data メソッドの引数に何も指定しなかった場合の話となります。get_context_data メソッドの引数にキーワード引数を指定してやれば、そのキーワード引数で指定したキー名・値を持つ要素がコンテキストに追加されるようになりますので、こういった方法でもコンテキストの要素の追加を行うことができます。

この辺りの実例は form_valid のオーバーライド でも紹介していますので、必要に応じて再度確認していただければと思い亜mす。

例えば下記のように get_context_data メソッドを実行すれば、text: 'Hello'value: 100 の2つの要素がコンテキストに追加されることになります。

get_context_dataへの引数指定
context = self.get_context_data(text='Hello', value=100)

コンテキストに存在する要素はテンプレートファイルから参照可能となりますので、ページに表示したいデータに応じてコンテキストに要素を追加するようにしてください。

FormView の利用例

最後に、ここまでの説明のまとめとして、FormView を継承するクラスでのビューの作成例を示していきたいと思います。

スポンサーリンク

プロジェクトとアプリの作成

最初にいつも通りプロジェクトを作成していきしょう!。

まず、適当な作業フォルダに移動したのち、下記コマンドでプロジェクトを作成します。今回はプロジェクトを formviewproject としています。

% django-admin startproject formviewproject

続いてアプリを作成していきます。先ほどのコマンドの実行で formviewproject というフォルダが作成されたはずなので、この formviewproject に下記の cd コマンドで移動します。

% cd formviewproject

続いて、下記コマンドを実行してアプリを作成します。今回はアプリを bmi としています。

% python manage.py startapp bmi

また、先ほど cd コマンドで移動後のフォルダの下にも formviewproject というフォルダがあるはずなので、そのフォルダの下にある settings.py を開き、下記のように INSTALLED_APPS のリストを変更します。これにより、アプリ bmi がプロジェクトに登録されることになります。

settings.pyの変更
INSTALLED_APPS = [
    'bmi', # 追加
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]

フォームの定義

次はフォームを定義していきます。今回は、身長(cm)と体重(kg)の入力を受け付けるため、下記のような height フィールドと weight フィールドを持つ BmiForm を定義したいと思います。定義先のファイルは bmi/forms.py となりますが、このファイルは存在しないためファイルを新規作成してから下記の様に中身を変更するようにしてください。

forms.py
from django import forms

class BmiForm(forms.Form):
    height = forms.FloatField()
    weight = forms.FloatField()

クラスベースビューの作成

続いて、このページの主題となっているクラスベースビューを作成していきます。今回は、ここまでの説明の通り FormView を継承するクラスを定義し、そのクラスをクラス変数の定義とメソッドのオーバーライドによってカスタマイズしていきます。

今回は、下記のように views.py を変更し、FormView を継承するクラスとして BmiView 定義したいと思います。

views.py
from django.views.generic import FormView
from .forms import BmiForm

class BmiView(FormView):
    form_class = BmiForm
    template_name = 'bmi/bmi_form.html'

    def form_valid(self, form):
        height = form.cleaned_data.get('height')
        weight = form.cleaned_data.get('weight')

        bmi = weight / ((height / 100) * (height / 100))

        return self.render_to_response(self.get_context_data(form=form, bmi=bmi))

form_class の定義

BmiView ではクラス変数 form_class を定義して BmiForm を指定しているため、BmiView でフォームを扱う際には先ほど forms.py に定義した BmiForm が利用されることになります。

より具体的にいうと、 BmiView が  GET リクエストを受け取った際には get メソッドが実行され、その中で BmiForm のインスタンスが生成されてコンテキストにセットされることになります。なので、テンプレートファイルから BmiForm のインスタンスを参照し、ページに height フィールドと weight フィールドを持つフォームを表示することができるようになります。

さらに、BmiView が  POST リクエストを受け取って post メソッドが実行される際にも BmiForm のインスタンスが生成されることになります。post メソッド実行時は、ユーザーがフォームに入力した各種フィールドのデータがセットされた状態のインスタンスが生成されるようになっており、このインスタンスを利用して、送信されてきたデータの妥当性の検証やフォームの再表示、さらには form_valid のオーバーライド で説明したように form_valid メソッドのオーバーライドによってフォームに入力された各種フィールドのデータを利用した処理などが実現可能となります。

このように、クラス変数 form_class に指定したフォームクラスは様々な場面で利用されます。

template_name の定義

また、BmiView ではクラス変数 template_name を定義して 'bmi/bmi_form.html' を指定しているため、BmiView が HTML を生成するような際には、このパスのテンプレートファイルが利用されることになります。この bmi/bmi_form.html は後述の説明の中で作成していきます。

form_valid の定義

さらに、BmiView では form_valid メソッドの定義を行なっています。このメソッドを定義する意味合いについては form_valid のオーバーライド で解説した通りで、実装例も同様になっているため、詳しくは form_valid のオーバーライド を参照していただければと思います。

前述の通り、form_valid はフォームから送信されてきたデータの妥当性の検証結果が OK であった場合に実行されるメソッドであり、FormViewform_valid ではリダイレクトレスポンスの返却しか行われないめ、form_valid のオーバーライドを行わないとフォームから送信されてきたデータは無視されることになります。フォームから送信されてきたデータを利用して処理を行いたい場合は、form_valid のオーバーライドが必須になります。

スポンサーリンク

ビューと URL とのマッピング

次は views.py で定義したビューと URL とのマッピングを行なっていきます。

まず、formviewproject フォルダの下にある urls.py を下記のように変更してください。

formviewproject/urls.py
from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('bmi/', include('bmi.urls')),
]

続いて、bmi フォルダの下に urls.py を新規作成し、中身を下記のように変更してください。

bmi/urls.py
from django.urls import path
from . import views

urlpatterns = [
    path('', views.BmiView.as_view(), name='bmi'),
]

テンプレートの作成

次はテンプレートファイルを作成していきます。ソースコードの変更としては、これが最後となります。

最初にテンプレートファイルを設置するのに必要となるフォルダを準備しておきましょう。まずアプリフォルダ(bmi フォルダ)の中に templates フォルダを作成してください。次に、その templates フォルダの中に bmi フォルダを作成します。これでフォルダの準備は完了です。

今回 views.py で定義した BmiView ではクラス変数 template_name を定義して 'bmi/bmi_form.html' を指定していますので、BmiView からは先ほど作成した bmi フォルダの中の bmi_form.html が利用されることになります。このファイルはまだ存在していないため、先ほど作成した bmi フォルダの中に bmi_form.html を新規作成します。

そして、このファイルの中身を下記のように変更して保存してください。これでテンプレートファイルの作成は完了になります。

bmi_form.html
<!doctype html>
<html lang="ja">
<head>
    <meta charset="utf-8">
    <title>Example of FormView</title>
</head>
<body>
    <main>
        <h2>BMIの計算</h2>
        {% if bmi %}
        <p>あなたのBMIは「{{ bmi }}」です</p>
        {% endif %}
        <form action="{% url 'bmi' %}" method="post">
            {% csrf_token %}
            <table>{{ form.as_table }}</table>
            <p><input type="submit" value="送信"></p>
        </form>
    </main>
</body>
</html>

bmi の参照

ポイントを説明しておくと、まず、このテンプレートファイルでは bmi と form という変数を参照しています。

bmi の参照箇所は下記で、コンテキストに 'bmi' キーの要素が存在する場合のみ下記の if が成立し、bmi の値が出力されるようになっています。GET リクエスト時は FormView が生成するコンテキスト で説明したようにコンテキストには 'bmi' キーの要素が存在しないため bmi の値は出力されないことになります。それに対し、POST リクエスト時には form_valid のオーバーライドによってコンテキストに 'bmi' キーの要素を追加するようにしているため bmi の値が出力されることになります。

bmiの参照
{% if bmi %}
<p>あなたのBMIは「{{ bmi }}」です</p>
{% endif %}

form の参照

そして、その bmi の値はフォームの height フィールドと weight フィールドに入力された数値から計算するようになっています。そして、この height フィールドと weight フィールドの入力欄を表示するために form を参照するようにしています。

FormView が生成するコンテキスト で説明したように、formBmiView ではクラス変数 form_class で指定した BmiForm のインスタンスであり、{{ form.as_table }} によってフォームをテーブル形式で出力しています。BmiForm では height フィールドと weight フィールドが定義されているため、これにより HTML 生成時には heightweight の入力フィールドがタグとして HTML に追加されることになります。

また、{{ form.as_table }} の下の行に存在するタグはボタン要素に対応するもので、このボタンをクリックすれば form タグの action 属性に指定した URL に POST リクエストが送信されることになります。そして、この action 属性には、urls.py によって BmiView にマッピングされた URL が指定されることになるため、BmiView に対して POST リクエストが送信されることになります。

そして、POST リクエストを受け取った BmiView は、フォームから送信されてきたデータの妥当性の検証を行い、その結果が OK の際に form_valid が実行されて height フィールドと weight フィールドに入力された数値から bmi を算出し、それをコンテキストの 'bmi' キーの値にセットした状態でテンプレートファイルからの HTML 生成が行われます。この際には、コンテキストに 'bmi' キーの要素が存在するため、下記によって bmi の出力が行われることになります。

bmiの参照
{% if bmi %}
<p>あなたのBMIは「{{ bmi }}」です</p>
{% endif %}

こういった流れが実現できるようにテンプレートファイルやビュー、さらには urls.py を作成するという点がポイントになると思います。

動作確認

最後に動作確認を行なっておきましょう!

最初に、manage.py が存在するフォルダで下記コマンドを実行します。

% python manage.py runserver

これにより、開発用ウェブサーバーが起動しますので、ウェブブラウザから作成したアプリのページを表示することが可能となります。

次に下記 URL をウェブブラウザで表示してみてください。

http://localhost:8000/bmi/

この URL をウェブブラウザで表示すれば下図のような heightweight の入力フィールドを持つフォームが表示されるはずです。これは BmiFormGET リクエストを受け取ったことによって表示されるフォームとなります。

GETリクエストを受け取ったFormViewによって表示されるフォーム

表示されたフォームの heightweight の入力フィールドに適当な数値を入力して 送信 ボタンを押せば今度は BmiFormPOST リクエストを受け取り、height フィールドと weight フィールドに入力された数値から bmi が計算され、その計算結果がページに表示されることになります。ちなみに、height フィールドに入力する数値の単位は cm、weight フィールドに入力する数値の単位は kg となります。

POSTリクエストを受け取ったFormViewによって表示される結果

動作確認としては以上となるのですが、ポイントは今回の例ではデータベースの操作が全く行われていない点にあります。CreateViewUpdateView ではデータベースへのレコードの保存が行われますし、ListViewDetailView ではデータベースからのレコードの取得が行われます。

それに対し、FormView の場合はデータベースの操作は必ずしも行われず、その場合はモデルクラスの定義やビューから利用するモデルクラスの設定等も不要になります。こんな感じで、データベースの操作を伴わないようなビューを実現する時に便利なのが FormView となります。

ただ、今回示した例はデータベースの操作は伴わないものになっていますが、実装によってはデータベースの操作を行うことも可能です。もちろん CreateViewUpdateView 等を利用する方がデータベースの操作を行うのが楽なのですが、FormView は開発者の実装によって様々なビューを実現可能な汎用性の高い View のサブクラス であることは覚えておくと良いと思います!

スポンサーリンク

まとめ

このページでは、FormView および FormView を継承したクラスベースビューの作り方について解説しました!

FormView はフォームを扱うビューを実現するための View のサブクラス であり、このクラスを継承することで、様々なフォームを扱うページを簡単に実現できます。

他のページで解説を行なっている CreateViewUpdateView 等とは異なり、FormView はデータベースの操作が行われることが前提となっていない View のサブクラス となります。そのため、データベースへの操作を伴わないようなフォームを扱うビューを実現するのに向いています。実装によっては FormView のサブクラスでデータベースへの操作を行うことも可能あり、汎用性の高い View のサブクラス となります。

データベースの操作が不要なウェブアプリやビューを開発していく上で便利な View のサブクラス となりますので、是非 FormView についても使いこなせるようになっておきましょう!

また、このサイトでは他の View のサブクラス についても説明していますので、他のページも是非読んでみてください!

DjangoのListViewの解説ページアイキャッチ【Django】ListViewの使い方(クラスベースビューでの一覧リストページの実現) DjangoのDetailViewの解説ページアイキャッチ【Django】DetailViewの使い方(クラスベースビューでの詳細ページの実現) CreateViewでのクラスベースビューの実現方法解説ページアイキャッチ【Django】CreateViewの使い方(クラスベースビューでの新規登録ページの実現) UpdateViewでのクラスベースビューの実現方法解説ページアイキャッチ【Django】UpdateViewの使い方(クラスベースビューでのレコード更新ページの実現) DeleteViewでのクラスベースビューの実現方法解説ページアイキャッチ【Django】DeleteViewの使い方(クラスベースビューでのレコード削除ページの実現) LoginViewの使い方の解説ページアイキャッチ【Django】LoginViewの使い方(クラスベースビューでのログインの実現) LogoutViewの使い方の解説ページアイキャッチ【Django】LogoutViewの使い方(クラスベースビューでのログアウトの実現)

同じカテゴリのページ一覧を表示