10
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Django2で動的にformを作る

Last updated at Posted at 2019-01-26

背景

djangoのformは色々処理してくれて便利なんですが基本的には決まったフォームを事前に定義する形式。
わたしはDB等でマスター設定されたデータから動的にformが作りたくて色々勉強しました。
つまりアンケートフォームのような指定したアンケートの設問をDBから取得してフォームを作るといった事がしたかったのです。

動的にformオブジェクトを生成される

ネットの海を徘徊しているといい文献がありました。
Dynamic Forms In Django
これを参考(ほぼ丸パクリ)しつつ動的フォームを作成したいと思います。

form.pyはpassのみ!?

マスタ値から動的に生成するのでコーディング時に要素は何も決まっていません。
よってEnqFormpassのみ(笑)

form.py
from django import forms

class EnqForm(forms.Form):
    pass

views.pyが肝になってきます

先ほどpassのみのform classを定義したので、そこに動的にFieldを割り当てていきます。
今回は簡単な紹介のためマスター値はDBからとってこず、事前に定義しています。

views.py
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 fieldsqs定義部がマスター値だと思ってください。
本番ではもちろんDBからModelを経由して値を取得します。

# create enquete form objectsが実際にフォームオブジェクトを定義している部分になります。
qstypeに応じて適切なfieldオブジェクトをform_item代入して行きます。
今回は説明のためtextとchoiceだけに絞ってます。しかもchoiceの選択肢も適当です。

DynamicEnqForm = type('DynamicEnqForm', (EnqForm,), form_item )

の部分でpassだけ定義したform classEnqFormが登場します。
先ほど作成したform_itemを要素としてEnqFormDynamicEnqFormとして生成されます。

# get enquete answersの部分はPOSTされた場合に値を取得する部分になります。
ここで次の処理を行えばOKですね。

# draw enquete formは動的に作成されたDynamicEnqFormにPOSTされた値をセットしなおして描画しています。

templateについて

dynamic.html
<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でパスをつなぐ

url.py
from django.urls import path

from . import views

urlpatterns = [
    path('', views.dynamic, name='index'),
]

最後にパスをつないで完了です。

この例では永久ループで処理自体は行われませんが、マスター相当のデータを変えることで動的にform生成できるようになりました(^^♪

後日談

このソースではMultipleChoiceFieldには対応できません。
ので、対応しました(笑)

参考文献

10
10
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?