revision-up-to: | 8961 (1.0) |
---|
Django には、 フォーム を複数のページに分割する 「フォームウィザード」アプリケーションがオプションで付いています。フォーム ウィザードは、フォームの状態を HTML の <input type="hidden"> フィー ルドにハッシュ化して保存し、最終的にフォームを提出するまで、サーバでフォー ムデータを処理させません。
フォームウィザードは、とても長いフォームを扱う必要があって、フォームを一つ のページに納めると不格好になってしまうような場合に使うとよいでしょう。 例えば、最初のページではユーザに重要な情報を尋ね、次のページでは比較的些細 な情報を訪ねるといったフォームで、フォームウィザードを使います。
「ウィザード (wizard)」という用語の意味は、 Wikipedia で解説されています 。
ユーザがウィザードを使うときの基本的なワークフローは、以下の通りです:
このアプリケーションは、可能な限り処理を自動化しています。基本的には、開発 者側で行う必要があるのは以下の 4 つの作業だけです:
フォームウィザード作成の最初のステップは、フォームクラスの作成です。 フォームの作成には django.forms の Form クラスを使わねばなりません。 Form クラスの開設 は forms のドキュメント を参照してください。
フォームクラスはコードベースのどこに置いてもかまいませんが、慣習的にはアプ リケーションフォルダの forms.py に置くことになっています。
例として、「コンタクトフォーム」のウィザードを作成してみましょう。このウィ ザードは、最初のページで送り手の e-mail アドレスとタイトルを入力させ、次の ページでメッセージ本体を入力させます。 forms.py は以下のようになる でしょう:
from django import forms
class ContactForm1(forms.Form):
subject = forms.CharField(max_length=100)
sender = forms.EmailField()
class ContactForm2(forms.Form):
message = forms.CharField(widget=forms.Textarea)
ウィザードは、ページ間のデータの受け渡しに HTML の隠しフィールドを使うため、 最後のページに表示するフォーム以外では FileField を使えません。
次のステップはフォームウィザードクラスの作成です。 フォームウィザードクラス は、 django.contrib.formtools.wizard.FormWizard のサブクラスにせね ばなりません。
フォームクラスと同様、フォームウィザードクラスはコードベースのどこに配置し てもかまいませんが、慣習的には forms.py の中に書きます。
サブクラスの定義で唯一必ず行わなければならないのは done() メソッドの実装で す。 done() メソッドは、 全ての フォームが提出され、フォームデータが有効であった場合に何をするかを 決めるメソッドです。このメソッドには二つの引数が渡されます:
以下の簡単な例では、データベースの操作を行わずに、単に検証済みのデータをテ ンプレートに表示しています:
from django.shortcuts import render_to_response
from django.contrib.formtools.wizard import FormWizard
class ContactWizard(FormWizard):
def done(self, request, form_list):
return render_to_response('done.html', {
'form_data': [form.cleaned_data for form in form_list],
})
このメソッドは POST で送信されるので、よき Web 市民たるべく、データの処 理後にはリダイレクトすべきでしょう。というわけで、もう一つの例を示します:
from django.http import HttpResponseRedirect
from django.contrib.formtools.wizard import FormWizard
class ContactWizard(FormWizard):
def done(self, request, form_list):
do_something_with_the_form_data(form_list)
return HttpResponseRedirect('/page-to-redirect-to-when-done/')
フォームウィザードの提供しているフックを詳しく知りたければ、後の フォームウィザードの特殊なメソッド を参照してください。
次に、ウィザードのフォームをレンダリングするためのテンプレートを作成する必 要があります。デフォルトでは、全てのフォームは forms/wizard.html と いう名前のテンプレートを使います。 (このテンプレート名は、後で説明する get_template() をオーバライドして 変更できます。このフックを使えば、各フォームで別々のテンプレートを使えます。)
テンプレートには、以下のコンテキストが渡されます:
辞書オブジェクト extra_context にオブジェクトを渡せば、コンテキスト に任意のオブジェクトを追加できます。 extra_context の指定には以下の 二つの方法があります:
テンプレート例の全体像は以下のようになります:
{% extends "base.html" %} {% block content %} <p>Step {{ step }} of {{ step_count }}</p> <form action="." method="post"> <table> {{ form }} </table> <input type="hidden" name="{{ step_field }}" value="{{ step0 }}" /> {{ previous_fields|safe }} <input type="submit"> </form> {% endblock %}
ウィザードを正しく動作させるには、 previous_fields, step_field およ び step0 を全て表示せねばなりません。
最後に、 urls.py にフォームウィザードオブジェクトを追加します。 ウィザードはフォームオブジェクトを引数に取ります:
from django.conf.urls.defaults import *
from mysite.testapp.forms import ContactForm1, ContactForm2, ContactWizard
urlpatterns = patterns('',
(r'^contact/$', ContactWizard([ContactForm1, ContactForm2])),
)
done() メソッドの他 にも、フォームウィザードは特殊なメソッドをいくつか提供しており、ウィザー ドの動作をカスタマイズできます。
こうしたメソッドには step という引数をとるものがあります。 step はゼロから始まるカウンタで、ウィザードの現在のステップを表しています。 (例えば、最初のフォームは 0 で、2 番目のフォームは 1 です。)
ステップを指定すると、フォームのプレフィクス文字列を返します。 デフォルトでは、単にステップ番号そのものを使います。詳しくは フォームプレフィクスのドキュメント <form-prefix> を参照してください。
デフォルトの実装は以下の通りです:
def prefix_for_step(self, step):
return str(step)
ハッシュチェックに失敗した際にテンプレートをレンダするためのメソッドで す。このメソッドをオーバライドする必要はほとんどありません。
デフォルトの実装は以下の通りです:
def render_hash_failure(self, request, step):
return self.render(self.get_form(step), request, step,
context={'wizard_error': 'We apologize, but your form has expired. Please continue filling out the form from this page.'})
受け取ったリクエストオブジェクトとフォームインスタンスに対するセキュリ ティハッシュを計算します。
デフォルトでは、フォームデータの MD5 ハッシュと SECRET_KEY を使います。このメソッドをオーバライドする必要はほとんどありません。
実装例を示します:
def security_hash(self, request, form):
return my_hash_function(request, form)
リクエストオブジェクトと、 URLconf を使って URL からキャプチャした args / kwargs を元に、ウィザードの状態を保存するためのフックで す。
デフォルトでは何もしません。
実装例を示します:
def parse_params(self, request, *args, **kwargs):
self.my_state = args[0]
指定されたステップのページ表示に使うテンプレート名を返します。
デフォルトでは、ステップに関係なく 'forms/wizard.html' を返します。
実装例を示します:
def get_template(self, step):
return 'myapp/wizard_%s.html' % step
get_template() が文字列のリストを返す場合、ウィザード はテンプレートシステムの select_template() 関数を使います。つま り、リスト中から最初に見つかったテンプレートを使います。 select_template() については テンプレートのドキュメントで解説しています 。例えば:
def get_template(self, step):
return ['myapp/wizard_%s.html' % step, 'myapp/wizard.html']
指定したステップのテンプレートをレンダし、 HttpResponseRedirect オブジェクトを返します。
カスタムのコンテキストを追加したり、 MIME タイプを変更したりしたければ、 このメソッドをオーバライドしてください。テンプレート名をオーバライドし たいだけなら、 get_template() を使ってください。
テンプレートは「フォームのテンプレートを作成する」で解説したコンテキス トを使ってレンダされます。
ウィザードの内部状態を変更するためのフックです。このフックには検証済み のフォームオブジェクトが渡されます。フォームには、必ずクリーニング済み で有効なデータが入っています。
このメソッドでフォームデータの内容を変更しては なりません 。内容に変 更を加えたければ、 self.extra_context を設定するか、 self.form_list に入っている提出済みフォームを入れ替えてください。
このメソッドは、フォームを提出する 全ての ステップでページのレンダリ ング時に呼び出されるので注意してください。
関数シグネチャを以下に示します:
def process_step(self, request, form, step): # ...
Aug 31, 2012