背景
djangoのformは色々処理してくれて便利なんですが基本的には決まったフォームを事前に定義する形式。
わたしはDB等でマスター設定されたデータから動的にformが作りたくて色々勉強しました。
つまりアンケートフォームのような指定したアンケートの設問をDBから取得してフォームを作るといった事がしたかったのです。
動的にformオブジェクトを生成される
ネットの海を徘徊しているといい文献がありました。
Dynamic Forms In Django
これを参考(ほぼ丸パクリ)しつつ動的フォームを作成したいと思います。
form.pyはpassのみ!?
マスタ値から動的に生成するのでコーディング時に要素は何も決まっていません。
よってEnqForm
はpass
のみ(笑)
from django import forms
class EnqForm(forms.Form):
pass
views.pyが肝になってきます
先ほどpass
のみのform classを定義したので、そこに動的にFieldを割り当てていきます。
今回は簡単な紹介のためマスター値はDBからとってこず、事前に定義しています。
from django.shortcuts import render
from .forms import EnqForm
from django import forms
# Create your views here.
def dynamic(request):
context = {}
content = {}
form_item = {}
# define enquete fields
qs = []
qs.append( {'title':'title1', 'description': 'note1', 'type': 'text' , 'required': False} )
qs.append( {'title':'title2', 'description': 'note2', 'type': 'text' , 'required': True} )
qs.append( {'title':'title3', 'description': 'note3', 'type': 'choice' , 'required': False} )
# create enquete form objects
no = 0
for q in qs:
if q['type'] == 'text':
form_item.update( { ('ans%d' % no): forms.CharField(label=q['title'], label_suffix=q['description'], required=q['required'], max_length=256) } )
elif q['type'] == 'choice':
form_item.update( { ('ans%d' % no): forms.ChoiceField(label=q['title'], label_suffix=q['description'], required=q['required'], choices=(('a','a'),('b','b'),('c','c'),('d','d'))) } )
no += 1
DynamicEnqForm = type('DynamicEnqForm', (EnqForm,), form_item )
# get enquete answers
if request.method == 'POST':
for key in request.POST.keys():
if key != 'csrfmiddlewaretoken':
content[key] = request.POST[key]
# draw enquete form
DynamicForm = DynamicEnqForm(content)
context['enq_form'] = DynamicForm
return render(request, "dynamic.html", context)
# define enquete fields
のqs
定義部がマスター値だと思ってください。
本番ではもちろんDBからModelを経由して値を取得します。
# create enquete form objects
が実際にフォームオブジェクトを定義している部分になります。
qs
のtype
に応じて適切なfieldオブジェクトをform_item
代入して行きます。
今回は説明のためtextとchoiceだけに絞ってます。しかもchoiceの選択肢も適当です。
DynamicEnqForm = type('DynamicEnqForm', (EnqForm,), form_item )
の部分でpass
だけ定義したform classEnqForm
が登場します。
先ほど作成したform_item
を要素としてEnqForm
がDynamicEnqForm
として生成されます。
# get enquete answers
の部分はPOSTされた場合に値を取得する部分になります。
ここで次の処理を行えばOKですね。
# draw enquete form
は動的に作成されたDynamicEnqForm
にPOSTされた値をセットしなおして描画しています。
templateについて
<form action="" method="POST">
{% csrf_token %}
{% for field in enq_form %}
<div>
<div>
<label for="{{ field.id_for_label }}">{{ field.label }}</label>
<p>{{ field.field.label_suffix }}</p>
{{ field }}
</div>
</div>
{% endfor %}
<input type="submit">
</form>
すいません。本当に必要な部分しか書いてません(笑)
url.pyでパスをつなぐ
from django.urls import path
from . import views
urlpatterns = [
path('', views.dynamic, name='index'),
]
最後にパスをつないで完了です。
この例では永久ループで処理自体は行われませんが、マスター相当のデータを変えることで動的にform生成できるようになりました(^^♪
後日談
このソースではMultipleChoiceFieldには対応できません。
ので、対応しました(笑)