このページでは、Django フレームワークで定義される FormView
の使い方について説明していきます。
この FormView
は View
というクラスのサブクラスの1つであり、ビューをクラスベースで作成する際に利用するクラスとなります。この View のサブクラス
を継承し、さらにクラス変数を定義したりメソッドをオーバーライドすることで、あなたが開発したいアプリに応じたビューを作成することが可能となります。
この辺りのクラスベースビューやクラスベースビューの作り方については下記ページで解説していますので、詳しくはこちらをご参照ください。
【Django入門15】クラスベースビューの基本今回は、多数存在する View のサブクラス
の中から FormView
に焦点を当てて解説を行なっていきます。
Contents
FormView
では、FormView
について解説していきたいと思います。
FormView
は、フォームを扱うページを実現する View のサブクラス
となります。
FormView
は django.views.generic
から import
して利用します。
from django.views.generic import FormView
FormView
は基本的にデータベース操作なし
実は、Django フレームワークにはフォームを扱うページを実現する View のサブクラス
が他にも用意されています。代表的なフォームを扱う View のサブクラス
としては、CreateView
や UpdateView
が挙げられます。これらに関しては下記のページで詳細を解説していますので、詳しくは下記ページを参照していただければと思います。
これらの CreateView
や UpdateView
と、FormView
との決定的な違いは、データベース操作を行うことを前提として定義されているかどうかという点にあります。
CreateView
はフォームを扱い、ユーザーがフォームから送信してきたデータからインスタンスを生成し、それをレコードとしてデータベースの特定のテーブルに新規登録することを前提とした View のサブクラス
となります。また、UpdateView
は、既にデータベースの特定のテーブルに存在しているレコードをユーザーがフォームから送信してきたデータから生成したインスタンス(レコード)で上書き・更新することを前提とした View のサブクラス
となります。
このように、これらの View のサブクラス
ではフォームから送信されてきたデータに基づいてデータベースの操作を行うことを前提としたクラスとなっています。そもそも、フォームとデータベースのレコードは密接に関連しており、ウェブアプリではデータベースの操作をユーザーからの入力に基づいて実施することを目的にフォームを扱うことが多いです。例えば、ユーザー登録を行うウェブアプリでは、ユーザ名やメールアドレス等を入力するフィールドを備えたフォームを表示し、そのフィールドに入力されたデータから生成したインスタンスをレコードとしてユーザー管理用のテーブルに保存が行われることになります。
また、Django において、データベースのテーブルはモデルクラスによって表現され、さらにテーブルのレコードは、そのモデルクラスのインスタンスとして扱われます。さらに、そもそもフォームはデータベースの特定のテーブルに対して操作を行うことを目的として利用する機会が多いため、Django にはフォームをモデルクラスに基づいて自動的に生成するような仕組みも存在しています。それが、下記ページでも紹介しているモデルフォームクラスになります。そして、前述で紹介した CreateView
や UpdateView
は、フォームとしてモデルフォームクラスを扱うことを前提としたものとなっています。
こういった前提に基づいて定義されている View のサブクラス
であるため、CreateView
や UpdateView
を利用することで、データベースの特定のテーブルに対してレコードの新規登録やレコードの更新を行うようなビューを簡単に実現することが可能となります。
しかし、機会が多いだけで、フォームを扱うからといって必ずしもデータベースの操作が行われるとは限りません。例えば、2つの数字の入力を受け付けるフォームを表示し、ユーザーから入力された2つの数字の掛け算結果を表示するようなビューの場合、わざわざ入力された数字をデータベースに保存する必要はなく、単にフォームから送信されてきた2つの数字の掛け算結果をページに表示してやれば良いだけになります。
このように、フォームは扱うもののデータベースの操作を伴わないビューを実現する場合、データベースの操作を行うことを前提として定義されている CreateView
や UpdateView
を利用するのでは、むしろ実現が難しくなります。
なので、データベースの操作を行うことを前提としていない View のサブクラス
を利用した方が良いです。そして、そのデータベースの操作を行うことを前提としていないフォームを扱うView のサブクラス
が、このページで説明する FormView
となります。
このように、FormView
はフォームは扱うもののデータベース操作を伴わないビューを実現するのに適した View のサブクラス
となります。
スポンサーリンク
送信されてきたデータの扱いはカスタマイズ次第
さて、前述の通り、CreateView
においては、フォームからのデータの送信があった際に送信されてきたデータからインスタンスを生成し、それをレコードとしたデータベースへの新規登録が行われるようになっています。また、UpdateView
においては、既にデータベースに登録済みのレコードを、送信されてきたデータに基づいて生成したレコード(インスタンス)で上書きする処理が行われるようになっています。ここで重要なポイントは、フォームから送信されてきたデータの扱いはクラスによって決まっているという点になります。
また、これらのクラスでは、上記の処理が完了した後に特定の URL へのリダイレクトレスポンスの返却が行われるようになっています。
では、FormView
ではフォームからのデータの送信があった際にどんな処理が行われるのでしょうか?
結論を言うと、特定の URL へのリダイレクトレスポンスの返却のみが行われます。つまり、フォームから送信されてきたデータは無視されます。これだとフォームを扱う意味がありませんね…。こんな View のサブクラス
がなぜ用意されているのでしょうか…。
この理由も単純で、FormView
は開発者からカスタマイズされることを前提とした作りとなっているからになります。もちろん、他の View のサブクラス
もカスタマイズされることを前提とした作りとなっているのですが、FormView
の場合はカスタマイズが必須で、特に form_valid
というメソッドのオーバーライドが必須となっています。
そして、このメソッドをオーバーライドして内部の処理を実装してやることで、開発者が自由にフォームから送信されてきたデータの扱いを決めることができます。具体的なオーバーライドの手順等は後述で解説していきますが、FormView
自体はフォームを扱う View のサブクラス
であるもののフォームから送信されてきたデータは基本的に無視されるようになっていること、さらにはカスタマイズ次第でフォームから送信されてきたデータの扱いを開発者自身が自由に決めることができることは覚えておくと良いと思います。
補足しておくと、CreateView
や UpdateView
でもカスタマイズによって送信されてきたデータの扱いを変更させることは可能です。例えば CreateView
でも、フォームから送信されてきたデータのレコードとしてのデータベースへの新規登録を行わないようにすることも可能です。
ただ、送信されてきたデータの扱いを変更してしまうと CreateView
や UpdateView
の役割が曖昧になってしまいますし、これらをわざわざ利用する意味も無くなってしまうことになるため、ここに関しては変更せずに利用するケースの方が多いと思います。重要なのは、各 View のサブクラス
の役割を理解し、それに応じて使い分けることです。
FormView
でのクラスベースビューの作り方
では、次は FormView
でクラスベースビューを作成する手順について説明していきます。
FormView
でのクラスベースビューの作り方に関しては、ここまで紹介してきた CreateView
や UpdateView
の場合とほぼ同様となります。
つまり、views.py
に FormView
を継承するクラスを定義し、そのクラスでクラス変数やメソッドを定義して FormView
側で定義されているクラス変数の上書き・メソッドのオーバーライドを行なうことで、ビューを作成していきます。これにより、FormView
の特徴を活かしながら自身のウェブアプリに応じたビューにカスタマイズしていくことが可能となります。
また、これも CreateView
や UpdateView
と同様に、FormView
ではリクエストに応じて get
メソッドや post
メソッドが実行されるようになっており、FormView
のクラス変数や他のメソッドは、これらの get
メソッドおよび post
メソッドから利用されるようになっています。したがって、FormView
のサブクラス側でクラス変数やメソッドを上書き・オーバーライドしてやれば、この get
メソッドと post
メソッドの動作を変化させることができます。
FormView
のサブクラスを定義する上で基本的に必須になるのは下記の3つとなります。
- 利用するフォームクラスを指定する
- 利用するテンプレートファイルを指定する
form_valid
のオーバーライドを行う
利用するフォームクラスを指定する
まず、FormView
はフォームを扱うために利用する View のサブクラス
であり、get
メソッドではフォームを表示するために、また post
メソッドではフォームから送信されてきたデータを扱うためにフォームクラスのインスタンスが利用されることになります。そして、そのフォームクラスを指定するのがクラス変数 form_class
の役割となります。
そのため、FormView
のサブクラスを定義する際にはクラス変数 form_class
を定義し、この form_class
にはフォームクラスを指定しておく必要があります。クラス変数 form_class
を定義していなかった場合、get
メソッドや post
メソッドが実行された際に下記のような例外が発生することになります。
'NoneType' object is not callable
利用するフォームを指定する必要があるのは CreateView
や UpdateView
の場合でも同様なのですが、これらの場合は form_class
以外のクラス変数の定義でもクラスで扱うフォームを指定することが可能でした(model
と fields
)。それに対し、FormView
の場合はフォームクラスを指定するクラス変数は form_class
の一択となります。
また、CreateView
や UpdateView
の場合は form_class
にはモデルフォームクラス(forms.ModelForm
を継承するクラス)を指定する必要がありましたが、FormView
の場合はモデルフォームクラスでも単なるフォームクラス(forms.Form
を継承するクラス)でも問題ありません。
スポンサーリンク
利用するテンプレートファイルを指定する
また、FormView
のサブクラスではクラス変数 template_name
の定義も必須となります。この template_name
には、基本的には FormView
のサブクラスが GET
メソッドのリクエストを受け取った時に利用するテンプレートファイルのパスを指定します。
そして、フォームを扱う場合、GET
メソッドのリクエストを受け取った時にはフォームを表示することになりますので、そのパスにはフォームが表示可能なテンプレートファイルを用意しておく必要があります。また、詳細は FormView が生成するコンテキスト で解説しますが、FormView
が GET
メソッドのリクエストを受け取った時に生成するコンテキストでは、form
キーにフォームクラスのインスタンスがセットされるようになっています。つまり、FormView
を利用してフォームを扱うビューを実現する場合、少なくとも下記のような form
タグを含むテンプレートファイルを用意しておく必要があります。
<form action="送信先のURL" method="post">
{% csrf_token %}
<table>{{ form.as_table }}</table>
<p><input type="submit" value="送信"></p>
</form>
上記はフォームのフィールドをテーブル形式で出力することを前提としたものになっているため、出力形式に合わせて変更する必要があるので注意してください。このあたりの Django におけるフォームの表示に関しては下記ページで詳しく解説していますので、必要に応じて下記ページを参照していただければと思います。
【Django入門5】フォームの基本CreateView
や UpdateView
の場合、template_name
を定義しなかった場合はクラスによって設定されているデフォルトのパスのテンプレートファイルが利用されることになります。なので、template_name
を定義しなくてもフォームの表示等を行うことは可能でした。それに対し、FormView
のサブクラスにはデフォルトのテンプレートファイルのパスが存在しないため、テンプレートファイルを利用するためにはクラス変数として template_name
を定義しておくことが必須となります。そして、template_name
を定義しなかった場合は get
メソッドが実行される際に例外が発生することになります。
form_valid
のオーバーライドを行う
さらに、送信されてきたデータの扱いはカスタマイズ次第 で説明したように、FormView
に関してはフォームから送信されてきたデータを扱う処理を開発者自身が実装する必要があります。これを行わないとフォームからデータが送信されてきても無視されてしまうことになります。
そして、このフォームから送信されてきたデータを扱う処理は FormView
のサブクラスの form_valid
メソッドに実装することになります。この form_valid
メソッドは FormView
で定義されていますので、要は form_valid
メソッドのオーバーライドを行ないます。
この form_valid
メソッドは FormView
だけでなく、CreateView
や UpdateView
などのフォームを扱う View のサブクラス
全般に用意されているメソッドです。そして、このメソッドはフォームから送信されてきたデータの妥当性の検証結果が OK の場合の処理を実装するメソッドとなります。
CreateView
や UpdateView
の場合、これらのビューで実現することは明白で、CreateView
の場合は、それが「フォームから送信されてきたデータをレコード(インスタンス)としてデータベースに新規登録すること」になります。そのため、CreateView
の form_valid
メソッドではレコードのデータベースへの新規登録処理が記述されています。そして、その後に特定の URL に対するリダイレクトレスポンスを返却する処理が記述されています。
UpdateView
の場合もほぼ同様ですが、UpdateView
の form_valid
メソッドではレコードの新規登録処理ではなく、データベースに登録済みのレコードの更新処理が記述されています。
これらに対し、FormView
の form_valid
メソッドには特定の URL に対するリダイレクトレスポンスを返却する処理しか記述されていません。そのため、前述の通り FormView
ではフォームからデータが送信されてきたとしても、そのデータに対する処理は一切行わないようになっています。正直これだとユーザーに対してフォームを表示してデータの入力受付を行う意味がないですね…。
つまり、FormView
のサブクラスで実現されるビューは、form_valid
をオーバーライドしない限りフォームを扱う意味のないビューしか実現できないことになります。
そのため、FormView
はサブクラス側で form_valid
メソッドを開発者自身が定義してオーバーライドすることを前提とした View のサブクラス
と考えて良いと思います。そもそも、FormView
の場合、CreateView
や UpdateView
のようにビューで実現することが明白ではなく、実現することが曖昧な View のサブクラス
となっています。違う言い方をすれば、FormView
は単にフォームを扱うための View のサブクラス
であり、そのフォームの利用目的は開発するウェブアプリによって異なります。なので、”開発するウェブアプリに応じて自由に form_valid
メソッドを開発者が定義してください” というスタンスの View のサブクラス
となっているのだと思います。
そのため、FormView
の場合、開発者自身が form_valid
メソッドを定義し、ウェブアプリの動作に合わせた処理を実装する必要があります。もちろん、特定の URL に対してリダイレクトを行うだけで良いのであれば form_valid
メソッドを定義してオーバーライドする必要もないのですが、それだとフォームによってユーザーからのデータの送信を受け付ける意味もありませんので、基本は form_valid
メソッドを定義してオーバーライドすることは必須であると考えて良いです。
FormView
のクラス変数
では、次は FormView
で定義されているクラス変数の紹介を行なっていきます。これらのクラス変数を FormView
を継承するクラスで定義し直して上書きすることでクラスの動作のカスタマイズを行うことが可能となります。
FormView
の場合、GET
メソッドのリクエストを受け取った際に get
メソッドが実行され、GET
メソッドのリクエストを受け取った際に post
メソッドが実行されることになるため、これらの get
メソッドや post
メソッドの動作を変化させることを目的にクラス変数の定義を行なっていくことになります。
スポンサーリンク
FormView
のクラス変数の一覧
この FormView
で定義されるクラス変数には下記のようなものが存在します。
- form_class (
CreateView
) - success_url (
CreateView
) - initial (
CreateView
) - prefix (
CreateView
) - template_name (
CreateView
) - extra_context (
ListView
) - template_engine (
ListView
) - response_class (
ListView
) - content_type (
ListView
)
各リンクをクリックすれば括弧内に示した View のサブクラス
の解説ページにジャンプするようにしています。基本的に、フォームを扱うための設定を行うクラス変数に関しては CreateView
の解説ページ、その他のクラス変数に関しては ListView
の解説ページにジャンプするようにしています。
FormView でのクラスベースビューの作り方 でも説明したように、FormView
ではクラス変数 form_class
と template_name
の定義が基本的に必須になるので注意してください。POST
リクエストを受け取り、さらにフォームから送信されてきたデータの妥当性の検証結果が OK の場合にリダイレクトを行うようにしたい場合は success_url
の定義も必須となります。
また、CreateView
や UpdateView
、さらには ListView
や DetailView
等には共通してクラス変数 model
が存在しますが、FormView
にはクラス変数 model
は存在しません。model
は、モデルクラスを指定することで操作対象のデータベースのテーブルを各クラスに指定する役割があったのですが、FormView
はデータベースの操作を行うことは前提となっていないためクラス変数 model
が存在しません。同様の理由で、FormView
にはデータベースに発行するクエリを指定する queryset
なども存在せず、上記で挙げた View のサブクラス
に比べると FormView
におけるクラス変数の数は少なくなっています。
FormView
のメソッド
次に FormView
の持つメソッドを紹介していきます。FormView
を継承したクラスを定義し、そのクラスで FormView
の持つメソッドをオーバーライドしてやることで FormView
とは異なる動作のクラスを実現することができるようになります。
FormView
のメソッド一覧
FormView
の持つメソッドの一覧は下記のようになります。あくまでもクラスのカスタマイズ目的でオーバーライドを行う可能性のあるものを挙げており、as_view
などのカスタマイズは行わないであろうメソッドは省略しています。
また、FormView
は get
メソッドと post
メソッドを持っており、メソッドが GET
のリクエストを受け取った場合は get
が実行され、メソッドが POST
のリクエストを受け取った場合は post
が実行されようになっています。そして、これらの get
や post
の中から各種メソッドが実行されるようになっています。
get
:リクエストのメソッドがGET
の場合の処理を実行するpost
:リクエストのメソッドがPOST
の場合の処理を実行するget_context_data
:テンプレートに渡すコンテキストを生成するget_form
:フォームクラスのインスタンスを取得するget_form_class
:フォームクラスを取得するget_form_kwargs
:フォームクラスのコンストラクタに指定するパラメータを取得するget_initial
:initial
への指定値を取得するget_prefix
:prefix
への指定値を取得するform_valid
:特定の URL へのリダイレクトレスポンスを返却する(妥当性の検証が OK の時に実行される)form_invalid
:再度フォームを表示する(妥当性の検証が NG の時に実行される)get_success_url
:リダイレクト先の URL を取得するget_template_names
:テンプレートファイルの名前を取得するrender_to_response
:レスポンスを返却する
これも FormView でのクラスベースビューの作り方 でも説明したように、FormView
の form_valid
ではリダイレクトレスポンスの返却のみが行われることになるため、フォームからユーザーがデータを送信してきたとしても、そのデータは無視されることになります。つまり、FormView
の form_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
メソッドは下記のように定義されています。
def form_valid(self, form):
"""If the form is valid, redirect to the supplied URL."""
return HttpResponseRedirect(self.get_success_url())
ご覧の通り、引数として self
と form
を受け取るようになっており、返却値は HttpResponseRedirect
のインスタンスとなっています。ただし、返却値に関しては、HttpResponse
のサブクラスであれば、別のクラスのインスタンスでも問題ありません。
上記の HttpResponseRedirect
に関しても HttpResponse
のサブクラスなので、リダイレクトを行いたい場合は、上記を真似て HttpResponseRedirect
のインスタンスを返却するようにしてやれば良いと思います。
また、HttpResponse
のサブクラスのインスタンスを返却する関数やメソッドも Django フレームワークには多数用意されていますので、その実行結果を返却するのでも良いです。例えば先ほど示したメソッド一覧の中にある render_to_response
に関しても HttpResponse
のサブクラスのインスタンスを返却するメソッドとなっています。また、関数ベースビューの時に利用することの多い render
も HttpResponse
のサブクラスのインスタンスを返却する関数となります。
ここまでをまとめると、form_valid
のオーバーライドを行う場合、まずは FormView
のサブクラスに下記のような形式の form_valid
を定義してやれば良いことになります。
def form_valid(self, form):
# 略
return HttpResponseのサブクラスのインスタンス
このように form_valid
を定義しておけば、FormView
のサブクラスが POST
リクエストを受け取り、受信したフォームの各種フィールドの値が妥当であると判断された際に、FormView
ではなく、FormView
のサブクラスの form_valid
が実行されるようになります。さらに、この form_valid
の返却値に応じたページ表示が行われることになります。
フォームから送信されてきたデータを取得する
続いて、form_valid
内部の処理を実装していきます。
この form_valid
のオーバーライドを行う目的は「フォームから送信されてきたデータを扱う」ことになります。どのように扱うかはウェブアプリによって異なりますが、フォームから送信されてきたデータを扱うためには、まずフォームから送信されてきたデータから各種フィールドの値を取得することが必要となります。
したがって、form_valid
には、フォームから送信されてきたデータから各種フィールドの値を取得する処理を記述する必要があります。フォームから送信されてきたデータは、妥当性の検証が行われた後に、引数 form
のデータ属性 cleaned_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 をレスポンスとして返却するケースが多いのではないかと思います。
例えば、先ほどの BmiForm
であれば、height
フィールドと weight
フィールドの値を取得してやれば BMI の計算が可能となりますので、その計算を行なって結果を HTML に埋め込んでレスポンスを返却してやることで、ウェブブラウザで表示するページに BMI が出力されるようになります。そして、これは FormView
のサブクラスで下記のように form_valid
を定義してやることで実現することができます。
def form_valid(self, form):
height = form.cleaned_data.get('height')
weight = form.cleaned_data.get('weight')
bmi = weight / ((height / 100) * (height / 100))
context = {
'bmi': bmi
}
return render(self.request, 'アプリ名/bmi.html', context)
基本的には、関数ベースビューの時と同様に render
関数を実行して HTML を生成し、render
関数の返却値である、その HTML をボディとする HttpResponse
のサブクラスのインスタンスを返却しているだけです。'アプリ名/bmi.html'
に、コンテキストの 'bmi'
を参照するための {{ bmi }}
が記述されたテンプレートファイルを用意しておけば、height
フィールドと weight
フィールドに入力された値から計算した BMI 値が出力されるページが表示されることになります。
前述の通り、form_valid
メソッドでは HttpResponse
のサブクラスのインスタンスを返却することが必須となります。逆に言うと、これさえ守れば、後は自由に form_valid
メソッドを実装して問題ありません。
また、FormView
のサブクラスではクラス変数 template_name
を定義することになるため、HTML 生成時に利用するテンプレートファイルのパスとして template_name
を指定することも可能です。ですが、この template_name
は、基本的には FormView
のサブクラスが GET
リクエストを受け取ってフォームを表示する時に利用するテンプレートファイルのパスを定義するクラス変数となります。なので、このパスにはフォーム出力用のテンプレートファイルが設置されることになります。それに対し、POST
リクエストを受け取った時に実行される form_valid
メソッドで利用するテンプレートファイルとしては、フォームから送信されてきたフィールドの値を用いた何らかの処理結果を出力することが多く、その場合はフォーム出力用ではなく結果出力用のテンプレートファイルが必要となります。
つまり、GET
リクエストを受け取った時と POST
リクエストを受け取った時とでは出力するデータが異なるため、テンプレートファイルとしても別々のものを用意しておく必要があります。もしくは、template_name
で指定するパスに、フォーム出力と結果出力の両方を兼ね備えたテンプレートファイルを設置しておくのでも良いです。この場合は用意するテンプレートファイルが1つで済みます。
ここまで説明してきたように、form_valid
メソッドは開発するウェブアプリに合わせて自由に定義することが可能ですが、「FormView
の form_valid
と引数・返却値を合わせた form_valid
を定義」し、さらに「フォームから送信されてきた各種フィールドに入力されたデータを引数 form
より取得」し、「その取得したデータを扱って何らかの処理を行う」という流れは共通になりますので、これに関しては是非覚えておいてください。
FormView
が生成するコンテキスト
続いて FormView
が生成するコンテキストについて説明しておきます。
FormView
が get_context_data
メソッドの中で生成するコンテキストには下記の要素が含まれます。この “フォームクラス” はクラス変数 form_class
で指定したクラスとなります。
'form'
: フォームクラスのインスタンス
このように、FormView
が生成するコンテキストにはフォームクラスのインスタンスしかセットされていません。
他のデータをコンテキストにセットしてテンプレートファイルから参照できるようにしたい場合は、ListView
の解説ページの extra_context で説明しているクラス変数 extra_context
を定義したり、get_context_data
のオーバーライドを行なったりしてコンテキストの要素を追加する必要があります。
また上記のように get_context_data
メソッドで 'form'
キーしか持たないコンテキストが生成されるのは、get_context_data
メソッドの引数に何も指定しなかった場合の話となります。get_context_data
メソッドの引数にキーワード引数を指定してやれば、そのキーワード引数で指定したキー名・値を持つ要素がコンテキストに追加されるようになりますので、こういった方法でもコンテキストの要素の追加を行うことができます。
この辺りの実例は、次の FormView の利用例 で紹介します。
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
がプロジェクトに登録されることになります。
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
となりますが、このファイルは存在しないためファイルを新規作成してから下記の様に中身を変更するようにしてください。
from django import forms
class BmiForm(forms.Form):
height = forms.FloatField()
weight = forms.FloatField()
クラスベースビューの作成
続いて、本題のクラスベースビューの定義を行っていきます。今回は、ここまでの説明の通り FormView
を継承するクラスを定義し、そのクラスをクラス変数の定義とメソッドのオーバーライドによってカスタマイズしていきます。
今回は、下記のように views.py
を変更し、FormView
を継承するクラスとして BmiView
定義したいと思います。
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(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_class
に指定したフォームクラスは様々な場面で利用されます。
template_name
の定義
また、BmiView
ではクラス変数 template_name
を定義して 'bmi/bmi_form.html'
を指定しているため、BmiView
が HTML を生成するような際には、このパスのテンプレートファイルが利用されることになります。この bmi/bmi_form.html
は後述の説明の中で作成していきます。
form_valid
の定義
さらに、BmiView
では form_valid
メソッドの定義を行なっています。form_valid のオーバーライドを行う 等でも説明したように、FormView
のサブクラスでは、この form_valid
メソッドの定義が基本的には必須となります。
上記の form_valid
メソッドのポイントは、返却値が「render_to_response
メソッドの返却値」になっているという点になります。まず、form_valid のオーバーライド で説明したように、form_valid
メソッドは HttpResponse
のサブクラスのインスタンスを返却する必要があります。この render_to_response
メソッドの返却値は HttpResponse
のサブクラスのインスタンスであるため、form_valid
からも HttpResponse
のサブクラスのインスタンスが返却できることになります。
また、この render_to_response
メソッドの引数には self.get_context_data(bmi=bmi)
が指定されています。get_context_data
は FormView
によって生成されたコンテキストを取得するメソッドになります。FormView が生成するコンテキスト でも説明したように、基本的には FormView
によって生成されたコンテキストにはフォームクラスのインスタンスしか含まれません。ですが、get_context_data
にキーワード引数を指定することで、そのコンテキストに要素を追加することが可能です。上記のように bmi=bmi
を指定すれば、コンテキストには値が「bmi
変数の値」である 'bmi'
キーの要素が追加されることになります。これにより、テンプレートファイルから bmi
変数の値が参照可能となります。
さらに、この render_to_response
では HTML の生成が行われるのですが、この HTML はクラス変数 template_name
で指定されたパスのテンプレートファイルを利用して生成されることになります。したがって、クラス変数 template_name
で指定されるパスのテンプレートファイルは、フォーム出力と結果出力(BMI 値の出力)の両方を兼ね備えたものである必要があります。このテンプレートファイルの作り方については後述で解説します。
スポンサーリンク
ビューと URL とのマッピング
次は views.py
で定義したビューと URL とのマッピングを行なっていきます。
まず、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
を新規作成し、中身を下記のように変更してください。
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
が GET
リクエストを受け取った時には、先ほど作成した bmi
フォルダの中の bmi_form.html
が利用されることになります。また、form_valid
メソッドのオーバーライドにより、POST
リクエストを受け取った時にも同じテンプレートファイルが利用されることになります。
そのため、まずは先ほど作成した bmi
フォルダの中に 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
の参照箇所は下記で、コンテキストに 'bmi'
キーの要素が存在する場合のみ下記の if
が成立し、bmi
の値が出力されるようになっています。GET
リクエスト時は FormView が生成するコンテキスト で説明したようにコンテキストには 'bmi'
キーの要素が存在しないため bmi
の値は出力されないことになります。それに対し、POST
リクエスト時には form_valid
のオーバーライドによってコンテキストに 'bmi'
キーの要素を追加するようにしているため bmi
の値が出力されることになります。
{% if bmi %}
<p>あなたのBMIは「{{ bmi }}」です</p>
{% endif %}
form
の参照
そして、その bmi
の値はフォームの height
フィールドと weight
フィールドに入力された数値から計算するようになっています。そして、この height
フィールドと weight
フィールドの入力欄を表示するために form
を参照するようにしています。
FormView が生成するコンテキスト で説明したように、form
は BmiView
ではクラス変数 form_class
で指定した BmiForm
のインスタンスであり、{{ form.as_table }}
によってフォームをテーブル形式で出力しています。BmiForm
では height
フィールドと weight
フィールドが定義されているため、これにより HTML 生成時には height
と weight
の入力フィールドがタグとして 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
の出力が行われることになります。
{% if bmi %}
<p>あなたのBMIは「{{ bmi }}」です</p>
{% endif %}
ということで、この bmi_form.html
では結果の出力とフォームの出力の両方が実現できるようになっています。今回は1つのテンプレートファイルのみでこれらを実現しましたが、form_valid のオーバーライド で説明したように、結果の出力用のテンプレートファイルとフォームの出力用のテンプレートファイルとを別々に用意し、それらのテンプレートファイルをメソッドに応じて使い分けるようにするのでも問題ありません。
動作確認
最後に動作確認を行なっておきましょう!
最初に、manage.py
が存在するフォルダで下記コマンドを実行します。
% python manage.py runserver
これにより、開発用ウェブサーバーが起動しますので、ウェブブラウザから作成したアプリのページを表示することが可能となります。
次に下記 URL をウェブブラウザで表示してみてください。
http://localhost:8000/bmi/
この URL をウェブブラウザで表示すれば下図のような height
と weight
の入力フィールドを持つフォームが表示されるはずです。これは BmiForm
が GET
リクエストを受け取ったことによって表示されるフォームとなります。
表示されたフォームの height
と weight
の入力フィールドに適当な数値を入力して 送信
ボタンを押せば今度は BmiForm
が POST
リクエストを受け取り、height
フィールドと weight
フィールドに入力された数値から bmi
が計算され、その計算結果がページに表示されることになります。ちなみに、height
フィールドに入力する数値の単位は cm、weight
フィールドに入力する数値の単位は kg となります。
動作確認としては以上となるのですが、ポイントは今回の例ではデータベースの操作が全く行われていない点にあります。CreateView
や UpdateView
ではデータベースへのレコードの保存が行われますし、ListView
や DetailView
ではデータベースからのレコードの取得が行われます。
それに対し、FormView
の場合はデータベースの操作は必ずしも行われず、その場合はモデルクラスの定義やビューから利用するモデルクラスの設定等も不要になります。こんな感じで、データベースの操作を伴わないようなビューを実現する時に便利なのが FormView
となります。
ただ、今回示した例はデータベースの操作は伴わないものになっていますが、実装によってはデータベースの操作を行うことも可能です。もちろん CreateView
や UpdateView
等を利用する方がデータベースの操作を行うのが楽なのですが、FormView
は開発者の実装によって様々なビューを実現可能な汎用性の高い View のサブクラス
であることは覚えておくと良いと思います!
スポンサーリンク
まとめ
このページでは、FormView
および FormView
を継承したクラスベースビューの作り方について解説しました!
FormView
はフォームを扱うビューを実現するための View のサブクラス
であり、このクラスを継承することで、様々なフォームを扱うページを簡単に実現できます。
他のページで解説を行なっている CreateView
や UpdateView
等とは異なり、FormView
はデータベースの操作が行われることが前提となっていない View のサブクラス
となります。そのため、データベースへの操作を伴わないようなフォームを扱うビューを実現するのに向いています。実装によっては FormView
のサブクラスでデータベースへの操作を行うことも可能あり、汎用性の高い View のサブクラス
となります。
データベースの操作が不要なウェブアプリやビューを開発していく上で便利な View のサブクラス
となりますので、是非 FormView
についても使いこなせるようになっておきましょう!
また、このサイトでは他の View のサブクラス
についても説明していますので、他のページも是非読んでみてください!