[Django] FormViewについて詳しく見てみる
今回はFormView
について詳しく見てみましょう。
Githubよりソースコードを拝借します。
django/django https://github.com/django/django/tree/stable/2.2.x
FormView
では、FormView
から
class FormView(TemplateResponseMixin, BaseFormView):
"""A view for displaying a form and rendering a template response."""
二つほどクラスを継承しています。TemplateResponseMixin
とBaseFormView
です。FormView
の中身はBaseFormView
で実装されていそうな雰囲気です。
続いてBaseFormView
を覗いていきます。
TemplateResponseMixin
はTemplateView
の回で紹介しているのでそちらをご覧ください。
BaseFormView
class BaseFormView(FormMixin, ProcessFormView):
"""A base view for displaying a form."""
こちらも二つほどクラスを継承しています。
それぞれどの部分を実装しているんでしょう?
まずは、ProcessFormView
の方から
ProcessFormView
class ProcessFormView(View):
"""Render a form on GET and processes it on POST."""
def get(self, request, *args, **kwargs):
"""Handle GET requests: instantiate a blank version of the form."""
return self.render_to_response(self.get_context_data())
def post(self, request, *args, **kwargs):
"""
Handle POST requests: instantiate a form instance with the passed
POST variables and then check if it's valid.
"""
form = self.get_form()
if form.is_valid():
return self.form_valid(form)
else:
return self.form_invalid(form)
def put(self, *args, **kwargs):
return self.post(*args, **kwargs)
View
クラスを継承して、それぞれのHttpメソッドに対応する処理が書かれています。FormView
ではget
に加えpost
,put
がサポートされているようです。
put
メソッドは中でpost
メソッドを呼んでいるだけですね。
post
メソッドでは、get_form
メソッドを利用してFormを取得しています。
ProcessFormView
ではget_form
メソッドは定義されていないので、FormMixin
の方で定義されているのでしょう。
取得したFormのis_valid
を呼んで入力が正しいか検証して、正しければform_valid
メソッドを、何か不備があればform_invalid
メソッドを呼ぶ仕組みになっています。
form_valid
,form_invalid
ともに、ProcessFormView
では定義されていないので、get_form
同様、FormMixin
の方で定義されているのでしょう。
いよいよFormMixin
です。
FormMixin
class FormMixin(ContextMixin):
"""Provide a way to show and handle a form in a request."""
initial = {}
form_class = None
success_url = None
prefix = None
def get_initial(self):
"""Return the initial data to use for forms on this view."""
return self.initial.copy()
def get_prefix(self):
"""Return the prefix to use for forms."""
return self.prefix
def get_form_class(self):
"""Return the form class to use."""
return self.form_class
def get_form(self, form_class=None):
"""Return an instance of the form to be used in this view."""
if form_class is None:
form_class = self.get_form_class()
return form_class(**self.get_form_kwargs())
def get_form_kwargs(self):
"""Return the keyword arguments for instantiating the form."""
kwargs = {
'initial': self.get_initial(),
'prefix': self.get_prefix(),
}
if self.request.method in ('POST', 'PUT'):
kwargs.update({
'data': self.request.POST,
'files': self.request.FILES,
})
return kwargs
def get_success_url(self):
"""Return the URL to redirect to after processing a valid form."""
if not self.success_url:
raise ImproperlyConfigured("No URL to redirect to. Provide a success_url.")
return str(self.success_url)
def form_valid(self, form):
"""If the form is valid, redirect to the supplied URL."""
return HttpResponseRedirect(self.get_success_url())
def form_invalid(self, form):
"""If the form is invalid, render the invalid form."""
return self.render_to_response(self.get_context_data(form=form))
def get_context_data(self, **kwargs):
"""Insert the form into the context dict."""
if 'form' not in kwargs:
kwargs['form'] = self.get_form()
return super().get_context_data(**kwargs)
先ほどのProcessFormView
のPOSTメソッドで呼び出されていたget_form
から順に見ていきましょう!
get_form
の中では、form_classをインスタンス化して返しています。
インスタンス化されるform_classは引数のform_classがNoneであればget_form_class
メソッドで取得しています。
get_form_class
メソッドではクラス変数のform_class
を返しています。
動的にform_classを変更したければ、get_form_class
かget_form
メソッド辺りをオーバーライドしてあげれば良いことがわかります。
form_classをインスタンス化する際に引数にget_form_kwargs
の戻り値を与えています。
この中はどうなっているのでしょう?
get_initial
,get_prefix
メソッドを呼び出してそれぞれinitial,prefixのキーを持つ辞書にして返しています。
さらに、アクセスがPOSTメソッド又は、PUTメソッドの場合はdata、filesを追加しています。
initialはフォームの初期値、prefixはformのフィールドのidにつけるprefixです。
prefixは一つのページで複数のフォームを利用する際にそれぞれのフォームを区別するためにつけたりします。
data,filesは、送信されてきたデータです。
これらをform_classに渡して、バリデーションだったりの処理を行わせます。
get_initial
の中身も見てみましょう。
とは言ってもやっていることはごく簡単で、クラス変数のinitialのコピーを作成して返しているだけです。
動的に、フォームの初期値を変更したい場合は、get_initial
をオーバーライドしてあげれば良さそうです。
ちなみに、クラス変数のinitialやget_initialはキーがフィールド名、値がフォームの初期値の辞書です。
そうして、初期化されたフォームクラスのインスタンスに対してis_valid
メソッドを呼び出して、フォームの入力内容を検証します。
検証に成功した場合は、form_valid
、失敗した場合には、form_invalid
を返すようになっています。
form_valid
の中では、get_success_url
の戻り値を引数に渡して、リダイレクトするようにしています。get_success_url
の中では、クラス変数のsuccess_url
を返しているだけです。
動的にリダイレクト先を変える場合にはget_success_url
をオーバーライドすれば良いですね。
form_invalid
の中では、form_classを再度テンプレートに渡してページを表示させています。
検証に失敗した、formにはエラーの情報が含まれているのでこれをテンプレート側で表示してあげればユーザーに検証の失敗理由が表示できます。
まとめ
- フォームに初期値を入れておくには
get_initial
をオーバーライドする。 - 動的にリダイレクト先を変えたい場合は
get_success_url
をオーバーライドする。 - 同じフォームを同一のページで利用する場合prefixを設定する。