このページでは、Django フレームワークで定義される FormView
の使い方について説明していきます。
この FormView
は View
というクラスのサブクラスの1つであり、ビューをクラスベースで作成する際に利用するクラスとなります。この View のサブクラス
を継承し、さらにクラス変数を定義したりメソッドをオーバーライドすることで、あなたが開発したいアプリに応じたビューを作成することが可能となります。
この辺りのクラスベースビューやクラスベースビューの作り方については下記ページで解説していますので、詳しくはこちらをご参照ください。
【Django入門14】クラスベースビューの基本今回は、多数存在する 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
を継承するクラス(サブクラス)を定義し、views.py
に定義したクラスにクラス変数やメソッドを定義することで 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
には forms.py
等で定義されているフォームクラスを指定しておく必要があり、クラス変数 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
のサブクラスが利用するテンプレートファイルのパスを指定します。
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
のインスタンスを返却するようになっています。
返却値は HttpResponseRedirect
のインスタンスとなっていますが、レスポンスが返却されるのであれば違うクラスのインスタンスを返却するのでも問題ありません。ここでレスポンスとは、 HttpResponse
のサブクラスのインスタンスとなります。
HttpResponseRedirect
は HttpResponse
のサブクラスの1つであり、リダイレクトを行いたい場合は、上記を真似て HttpResponseRedirect
のインスタンスを返却するので問題ありません。それ以外の場合は、目的に応じた HttpResponse
のサブクラスを選択し、そのインスタンスを返却するようにしてやれば良いです。
また、HttpResponse
のサブクラスのインスタンスを返却する関数やメソッドも Django フレームワークには多数用意されていますので、その実行結果を返却するのでも良いです。例えば先ほど示したメソッド一覧の中にある render_to_response
に関しても HttpResponse
のサブクラスのインスタンスを返却するメソッドとなっています。また、関数ベースビューの時に利用することの多い render
関数も HttpResponse
のサブクラスのインスタンスを返却するメソッドとなります。
ここまでをまとめると、form_valid
のオーバーライドを行う場合、まずは FormView
のサブクラスに下記のような形式の form_valid
を定義してやれば良いことになります。
def form_valid(self, form):
# 略
return HttpResponseのサブクラスのインスタンス
フォームから送信されてきたデータを取得する
続いて、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))
return self.render_to_response(self.get_context_data(form=form, bmi=bmi))
前述の通り、form_valid
では HttpResponse
のサブクラスのインスタンスを返却することが必要です。で、上記で return
している render_to_response
の返却値は HttpResponse
のサブクラスのインスタンスとなりますので、このような処理を記述しておけば “HttpResponse
のサブクラスのインスタンスを返却すること” を満たすことができます。
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
となります。
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
のオーバーライドの一例で、実際には開発したいウェブアプリに応じた実装を行う必要があります。ただ、上記のように「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
メソッドの引数にキーワード引数を指定してやれば、そのキーワード引数で指定したキー名・値を持つ要素がコンテキストに追加されるようになりますので、こういった方法でもコンテキストの要素の追加を行うことができます。
この辺りの実例は form_valid のオーバーライド でも紹介していますので、必要に応じて再度確認していただければと思い亜mす。
例えば下記のように get_context_data
メソッドを実行すれば、text: 'Hello'
、value: 100
の2つの要素がコンテキストに追加されることになります。
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
がプロジェクトに登録されることになります。
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(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 であった場合に実行されるメソッドであり、FormView
のform_valid
ではリダイレクトレスポンスの返却しか行われないめ、form_valid
のオーバーライドを行わないとフォームから送信されてきたデータは無視されることになります。フォームから送信されてきたデータを利用して処理を行いたい場合は、form_valid
のオーバーライドが必須になります。
スポンサーリンク
ビューと 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
からは先ほど作成した bmi
フォルダの中の bmi_form.html
が利用されることになります。このファイルはまだ存在していないため、先ほど作成した 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'
キーの要素が存在する場合のみ下記の 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 %}
こういった流れが実現できるようにテンプレートファイルやビュー、さらには urls.py
を作成するという点がポイントになると思います。
動作確認
最後に動作確認を行なっておきましょう!
最初に、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 のサブクラス
についても説明していますので、他のページも是非読んでみてください!