私はオンラインストアのようなものに取り組んでいます。顧客がアイテムを購入するフォームを作成していますが、購入したいアイテムの数を選択できます。しかし、彼女が購入するすべてのアイテムでは、その色が何であるかを選択する必要があります。したがって、フィールドの数は一定ではありません。顧客が3つのアイテムを購入した場合、3 <select>
色を選択するためのボックス。7個のアイテムを購入した場合、7個の<select>
ボックス。
JavaScriptを使用して、HTMLフォームフィールドを表示および非表示にします。しかし、Djangoフォームクラスでこれをどのように処理しますか?フォームフィールドはクラス属性であることがわかります。したがって、フォームインスタンスがカラーフィールドといくつかの7。
どんな手掛かり?
Jacob Kaplan-Mossには、動的フォームフィールドに関する広範な記事があります。 http://jacobian.org/writing/dynamic-form-generation/
基本的に、インスタンス化中にフォームのself.fields
辞書にアイテムを追加します。
別のオプションがあります: formset ?フィールドはすべて同じであるため、まさにそれがフォームセットの使用目的です。
Django adminはFormSet
s +少しのJavaScriptを使用して任意の長さのインラインを追加します。
class ColorForm(forms.Form):
color = forms.ChoiceField(choices=(('blue', 'Blue'), ('red', 'Red')))
ColorFormSet = formset_factory(ColorForm, extra=0)
# we'll dynamically create the elements, no need for any forms
def myview(request):
if request.method == "POST":
formset = ColorFormSet(request.POST)
for form in formset.forms:
print "You've picked {0}".format(form.cleaned_data['color'])
else:
formset = ColorFormSet()
return render(request, 'template', {'formset': formset}))
<script>
$(function() {
// this is on click event just to demo.
// You would probably run this at page load or quantity change.
$("#generate_forms").click(function() {
// update total form count
quantity = $("[name=quantity]").val();
$("[name=form-TOTAL_FORMS]").val(quantity);
// copy the template and replace prefixes with the correct index
for (i=0;i<quantity;i++) {
// Note: Must use global replace here
html = $("#form_template").clone().html().replace(/__prefix_/g', i);
$("#forms").append(html);
};
})
})
</script>
<form method="post">
{{ formset.management_form }}
<div style="display:none;" id="form_template">
{{ formset.empty_form.as_p }}
</div><!-- stores empty form for javascript -->
<div id="forms"></div><!-- where the generated forms go -->
</form>
<input type="text" name="quantity" value="6" />
<input type="submit" id="generate_forms" value="Generate Forms" />
あなたはそれを好きなようにできます
def __init__(self, n, *args, **kwargs):
super(your_form, self).__init__(*args, **kwargs)
for i in range(0, n):
self.fields["field_name %d" % i] = forms.CharField()
そして、フォームインスタンスを作成するとき、あなたはただ
forms = your_form(n)
それは単なる基本的な考え方であり、コードを好きなように変更できます。 :D
私がそれをする方法は次のとおりです:
次のように、froms.Form
から継承する「空の」クラスを作成します。
class ItemsForm(forms.Form):
pass
実際のフォームであるフォームオブジェクトのディクショナリを作成します。その構成はコンテキストに依存します(たとえば、外部モジュールからインポートできます)。例えば:
new_fields = {
'milk' : forms.IntegerField(),
'butter': forms.IntegerField(),
'honey' : forms.IntegerField(),
'eggs' : forms.IntegerField()}
ビューでは、python native "type"関数を使用して、可変数のフィールドを持つFormクラスを動的に生成できます。
DynamicItemsForm = type('DynamicItemsForm', (ItemsForm,), new_fields)
コンテンツをフォームに渡し、テンプレートでレンダリングします。
Form = DynamicItemsForm(content)
context['my_form'] = Form
return render(request, "demo/dynamic.html", context)
「コンテンツ」は、フィールド値の辞書です(たとえば、request.POSTでも可能です)。あなたは私の例全体が説明されているのを見ることができます here 。
別のアプローチ:通常のフィールド初期化フローを壊すのではなく、ミックスインでフィールドをオーバーライドし、生成されるたびに追加されるgenerate_dynamic_fieldsの動的フィールドのOrderedDictを返すことができます。
from collections import OrderedDict
class DynamicFormMixin:
_fields: OrderedDict = None
@property
def fields(self):
return self._fields
@fields.setter
def fields(self, value):
self._fields = value
self._fields.update(self.generate_dynamic_fields())
def generate_dynamic_fields(self):
return OrderedDict()
簡単な例:
class ExampleForm(DynamicFormMixin, forms.Form):
instance = None
def __init__(self, instance = None, data=None, files=None, auto_id='id_%s', prefix=None, initial=None,
error_class=ErrorList, label_suffix=None, empty_permitted=False, field_order=None,
use_required_attribute=None, renderer=None):
self.instance = instance
super().__init__(data, files, auto_id, prefix, initial, error_class, label_suffix, empty_permitted, field_order,
use_required_attribute, renderer)
def generate_dynamic_fields(self):
dynamic_fields = OrderedDict()
instance = self.instance
dynamic_fields["dynamic_choices"] = forms.ChoiceField(label=_("Number of choices"),
choices=[(str(x), str(x)) for x in range(1, instance.number_of_choices + 1)],
initial=instance.initial_choice)
return dynamic_fields