DjangoのForm wizardを使ってみる
djangoでは複数ページにまたがるフォームを処理するためにForm wizardという仕組みが用意されてます。
練習のため、できるだけシンプルな使い方をして、動作を確認してみました。
まずFormが必要です。
適当に2つ用意します。
forms.py
class AddressForm(forms.Form): name = forms.CharField(max_length=255) prefecture = forms.ChoiceField(choices=JP_PREFECTURES, widget=JPPrefectureSelect()) address = forms.CharField(max_length=255) class PaymentForm(forms.Form): payment_methd = forms.ChoiceField(choices=((0, u'クレカ'), (1, u'代引き')))
つぎにWizardViewを用意します。
views.py
class OrderWizard(SessionWizardView): form_list = [AuthenticationForm, AddressForm, PaymentForm] def done(self, form_list, **kwargs): # 入力値を使って、登録処理などを行い、 # 実際はリダイレクトさせる return render_to_response('apps/done.html', { 'form_data': [form.cleaned_data for form in form_list], })
やっぱ2つだと面白くないので、django標準のAuthenticationFormも使って3ページにまたがるフォームにしてみます。
done関数は、すべてのフォームの入力が済んだあとに呼び出される関数で、必ず実装する必要があります。
テンプレートを用意します。
フォームごとに別々のテンプレートを用意することができますが、最もシンプルな使い方として、1つのテンプレートだけで動かしてみます。
formtools/wizard/wizard_form.html
{% extends "base.html" %} {% load i18n %} {% block head %} {{ wizard.form.media }} {% endblock %} {% block content %} <p>Step {{ wizard.steps.step1 }} of {{ wizard.steps.count }}</p> <form action="" method="post">{% csrf_token %} <table> {{ wizard.management_form }} {% if wizard.form.forms %} {{ wizard.form.management_form }} {% for form in wizard.form.forms %} {{ form }} {% endfor %} {% else %} {{ wizard.form }} {% endif %} </table> {% if wizard.steps.prev %} <button name="wizard_goto_step" type="submit" value="{{ wizard.steps.first }}">{% trans "first step" %}</button> <button name="wizard_goto_step" type="submit" value="{{ wizard.steps.prev }}">{% trans "prev step" %}</button> {% endif %} <input type="submit" value="{% trans "submit" %}"/> </form> {% endblock %}
djangoのドキュメントからもってきました。
これを見るとwizard.management_formを表示させる必要があること、formsetに対応していることがわかります。
自前で用意するときは、これを参考にすればいいでしょう。
あとdone.htmlを適当に用意して、最後に入力値を確認できるようにしておきます。
<!DOCTYPE html> <html> <head> <title></title> </head> <body> {{ form_data }} </body> </html>
urls.pyにルーティングの設定をします。
as_viewにFormのリストを渡します。
urls.py
url(r'^order/$', OrderWizard.as_view([AuthenticationForm, AddressForm, PaymentForm]))
これで準備完了です。
動かしてみます。
順番にフォーム入力して、最後にdoneで全ての入力値がvalidな状態で渡されていることがわかります。
urlはすべて一緒です。
どのように順番を制御しているのか?
みてみると
{{ wizard.management_form }}
が
<input id="id_order_wizard-current_step" name="order_wizard-current_step" type="hidden" value="0">
このように表示されてます。このhidden入力値で表示するフォームを制御してるんですね。
ほかに入力値をhiddenで引き回したりはしていません。
ViewをSessionWizardViewを継承して作成しているので、入力値はSessionに保存されてます。
# 最初のフォーム表示時のセッションデータ {'wizard_order_wizard': {'step_files': {}, 'step': u'0', 'extra_data': {}, 'step_data': {}}} # 2つめのフォーム表示時のセッションデータ {'wizard_order_wizard': {'step_files': {u'0': {}}, 'step': u'1', 'extra_data': {}, 'step_data': {u'0': {u'0-password': ['pass'], u'csrfmiddlewaretoken': [u'aIektqp3Q9vQBexwaUOSsuz809NWjPKa'], u'order_wizard-current_step': [u'0'], u'0-username': [u'user']}}}} # 3つめのフォーム表示時のセッションデータ {'wizard_order_wizard': {'step_files': {u'1': {}, u'0': {}}, 'step': u'2', 'extra_data': {}, 'step_data': {u'1': {u'csrfmiddlewaretoken': [u'aIektqp3Q9vQBexwaUOSsuz809NWjPKa'], u'1-prefecture': [u'hokkaido'], u'1-name': [u'brainstorm'], u'order_wizard-current_step': [u'1'], u'1-address': [u'test']}, u'0': {u'0-password': [u'pass'], u'csrfmiddlewaretoken': [u'aIektqp3Q9vQBexwaUOSsuz809NWjPKa'], u'order_wizard-current_step': [u'0'], u'0-username': [u'user']}}}} # doneページの表示時のセッションデータ {'wizard_order_wizard': {'step_files': {}, 'step': None, 'extra_data': {}, 'step_data': {}}}
他にCookieWizardViewというのもあるので今度試してみたいと思います。
ぱっと見ややこしそうな印象だったんですが、使ってみるとなんとなくわかりました。
フックポイントがいっぱいあるので、それもそのうち試したい。