我的表格中有3个字段。
我有一个提交按钮和一个“添加额外字段”按钮。
我知道我可以在表单类中使用__init__
方法添加字段。
我是Python和Django的新手,我遇到了一个初学者问题: 我的问题是:
当我点击“添加附加字段”按钮时,添加附加字段的过程是什么?
表格是否必须再次渲染?
我如何以及何时致电__init__
或者我是否必须致电它?
如何将参数传递给__init__
?
答案 0 :(得分:59)
您的表单必须根据从POST传递给它的一些变量构建(或盲目检查属性)。每次重新加载视图时都会构造表单本身,错误与否,因此HTML需要包含有关构建正确数量的字段以进行验证的字段数的信息。
我会以FormSet
的工作方式来看待这个问题:有一个隐藏字段,其中包含活动表单的数量,每个表单名称都以表单索引为前缀。
事实上,你可以创建一个字段FormSet
https://docs.djangoproject.com/en/dev/topics/forms/formsets/#formsets
如果您不想使用FormSet
,您可以自己创建此行为。
这是一个从头开始的 - 它应该给你一些想法。它还回答了有关将参数传递给__init__
的问题 - 您只需将参数传递给对象构造函数:MyForm('arg1', 'arg2', kwarg1='keyword arg')
class MyForm(forms.Form):
original_field = forms.CharField()
extra_field_count = forms.CharField(widget=forms.HiddenInput())
def __init__(self, *args, **kwargs):
extra_fields = kwargs.pop('extra', 0)
super(MyForm, self).__init__(*args, **kwargs)
self.fields['extra_field_count'].initial = extra_fields
for index in range(int(extra_fields)):
# generate extra fields in the number specified via extra_fields
self.fields['extra_field_{index}'.format(index=index)] = \
forms.CharField()
def myview(request):
if request.method == 'POST':
form = MyForm(request.POST, extra=request.POST.get('extra_field_count'))
if form.is_valid():
print "valid!"
else:
form = MyForm()
return render(request, "template", { 'form': form })
<form>
<div id="forms">
{{ form.as_p }}
</div>
<button id="add-another">add another</button>
<input type="submit" />
</form>
<script>
form_count = Number($("[name=extra_field_count]").val());
// get extra form count so we know what index to use for the next item.
$("#add-another").click(function() {
form_count ++;
element = $('<input type="text"/>');
element.attr('name', 'extra_field_' + form_count);
$("#forms").append(element);
// build element and append it to our forms container
$("[name=extra_field_count]").val(form_count);
// increment form count so our view knows to populate
// that many fields for validation
})
</script>
答案 1 :(得分:10)
我遇到过必须使用动态字段动态创建表单的情况。我用这个技巧做了:
from django import forms
...
dyn_form = type('DynForm', # form name is irrelevant
(forms.BaseForm,),
{'base_fields': fields})
有关详细信息,请参阅此链接: Dynamic Forms
但除此之外我还必须注入字段,即在创建表单类后动态添加字段。
dyn_form.base_fields['field1'] = forms.IntegerField(widget=forms.HiddenInput(), initial=field1_val)
dyn_form.base_fields['field2'] = forms.CharField(widget=forms.HiddenInput(), initial=field2_val)
这很有效。
答案 2 :(得分:4)
没有javascript和字段类型的方法没有在js中描述:
<强> PYTHON 强>
def __init__(self, *args, **kwargs):
super(Form, self).__init__(*args, **kwargs)
##ajouts des champs pour chaque chien
for index in range(int(nb_dogs)):
self.fields.update({
'dog_%s_name' % index: forms.CharField(label=_('Name'), required=False, max_length=512),
})
def fields_dogs(self):
fields = []
for index in range(int(nb_dogs)):
fields.append({
'name': self['dog_%s_name' % index],
})
return fields
<强> TEMPLATE 强>
{% for field_dog in f.fields_dogs %}
<thead>
<tr>
<th style="background-color: #fff; border-width: 0px;"></th>
<th>{% trans 'Dog' %} #{{forloop.counter}}</th>
<th>{% trans 'Name' %}</th>
</tr>
</thead>
<tbody>
<tr>
<td style="background-color: #fff; border-width: 0px;"></td>
<td style="background-color: #fff; border-width: 0px;"></td>
<td>{{field_dog.name.errors}}{{field_dog.name}}</td>
</tr>
<tr>
<td style="padding: 10px; border-width: 0px;"></td>
</tr>
</tbody>
{% endfor %}
答案 3 :(得分:4)
这个答案是基于@ Yuji&#39; Tomita&#39; Tomita的几个改进和变化。
虽然@ Yuji&Tomita&Tomita的回答非常棒,但为了构建&#34;以django形式添加额外字段,可以很好地说明要遵循的方向。功能,我发现代码的某些部分存在一些问题。
在这里,我根据@ Yuji&#39; Tomita&#39; Tomita的初步提案提供我的工作代码:
观看次数(在view.py文件中)
视图中没有任何改变:
def myview(request):
if request.method == 'POST':
form = MyForm(request.POST, extra=request.POST.get('total_input_fields'))
if form.is_valid():
print "valid!"
else:
form = MyForm()
return render(request, "template", { 'form': form })
表单(在form.py文件中)
class MyForm(forms.Form):
empty_layer_name = forms.CharField(max_length=255, required=True, label="Name of new Layer")
total_input_fields = forms.CharField(widget=forms.HiddenInput())
def __init__(self, *args, **kwargs):
extra_fields = kwargs.pop('extra', 0)
# check if extra_fields exist. If they don't exist assign 0 to them
if not extra_fields:
extra_fields = 0
super(MyForm, self).__init__(*args, **kwargs)
self.fields['total_input_fields'].initial = extra_fields
for index in range(int(extra_fields)):
# generate extra fields in the number specified via extra_fields
self.fields['extra_field_{index}'.format(index=index)] = forms.CharField()
模板HTML
<form id="empty-layer-uploader" method="post" enctype="multipart/form-data" action="{% url "layer_create" %}">
<div id="form_empty_layer">
<input type="hidden" name="csrfmiddlewaretoken" value="{{ csrf_token }}">
{{ form.errors }}
{{ form.non_field_errors }}
{% if errormsgs %}
{% for value in errormsgs %}
</p> {{ value }} </p>
{% endfor %}
{% endif %}
{% for error in form_empty_layer.non_field_errors %}
{{ error }} </br>
{% endfor %}
</br>
{% for field in form_empty_layer.visible_fields %}
{{ field }} </br>
{% endfor %}
</div>
</br>
<button type="button" id="add-another">add another</button> </br> </br>
<button type="submit" id="empty-layer-button" name="emptylayerbtn">Upload</button>
</br></br>
// used in order to save the number of added fields (this number will pass to forms.py through the view)
<input type="text" name="total_input_fields"/>
</form>
模板Jquery
// check how many times elements with this name attribute exist: extra_field_*
form_count = $('input[name*="extra_field_*"]').length;
// when the button 'add another' is clicked then create a new input element
$(document.body).on("click", "#add-another",function(e) {
new_attribute = $('<input type="text"/>');
// add a name attribute with a corresponding number (form_count)
new_attribute.attr('name', 'extra_field_' + form_count);
// append the new element in your html
$("#form_empty_layer").append(new_attribute);
// increment the form_count variable
form_count ++;
// save the form_count to another input element (you can set this to invisible. This is what you will pass to the form in order to create the django form fields
$("[name=total_input_fields]").val(form_count);
})
答案 4 :(得分:0)
<强> forms.py 强>
class LicmodelForm1(forms.Form):
othercolumsvalue = forms.IntegerField(min_value=0, initial=0)
class LicmodelForm2(forms.Form):
def __init__(self, *args, **kwargs):
extra_fields = kwargs.pop('extra', 0)
super(LicmodelForm2, self).__init__(*args, **kwargs)
for index in range(int(extra_fields)):
# generate extra fields in the number specified via extra_fields
self.fields['othercolums_{index}'.format(index=index)] = \
forms.CharField()
self.fields['othercolums_{index}_nullable'.format(index=index)] = \
forms.BooleanField(required=False)
对于多步骤表单,您不需要额外的字段,在此代码中,我们在第一步中使用 othercolumsvalue 字段。
<强> views.py 强>
class MyFormTool(SessionWizardView):
def get_template_names(self):
return [TEMPLATES[self.steps.current]]
def get_context_data(self, form, **kwargs):
context = super(MyFormTool, self).get_context_data(form=form, **kwargs)
data_step1 = self.get_cleaned_data_for_step('step1')
if self.steps.current == 'step2':
#prepare tableparts for the needLists
needList_counter = 0
for i in self.wellKnownColums:
if data_step1[i] is True:
needList_counter = needList_counter + 1
pass
#prepare tableparts for othercolums
othercolums_count = []
for i in range(0, data_step1['othercolumsvalue']):
othercolums_count.append(str(i))
context.update({'step1': data_step1})
context.update({'othercolums_count': othercolums_count})
return context
def get_form(self, step=None, data=None, files=None):
form = super(MyFormTool, self).get_form(step, data, files)
if step is None:
step = self.steps.current
if step == 'step2':
data = self.get_cleaned_data_for_step('step1')
if data['othercolumsvalue'] is not 0:
form = LicmodelForm2(self.request.POST,
extra=data['othercolumsvalue'])
return form
def done(self, form_list, **kwargs):
print('done')
return render(self.request, 'formtools_done.html', {
'form_data' : [form.cleaned_data for form in form_list],
})
通过覆盖 get_form()和 get_context_data()函数,您可以覆盖表单,因为它会被渲染。您的模板文件不再需要JavaScript:
{% if step1.othercolumsvalue > 0 %}
<tr>
<th>Checkbox</th>
<th>Columname</th>
</tr>
{% for i in othercolums_count %}
<tr>
<td><center><input type="checkbox" name="othercolums_{{ i }}_nullable" id="id_othercolums_{{ i }}_nullable" /></center></td>
<td><center><input type="text" name="othercolums_{{ i }}" required id="id_othercolums_{{ i }}" /></center></td>
</tr>
{% endfor %}
{% endif %}
由于名称相同,动态制作的第二步中的字段也从表单工具中重新调整。但要实现这一目标,您必须解决for-each模板循环,如您所见:
来自get_context_data() - 函数的
othercolums_count = []
for i in range(0, data_step1['othercolumsvalue']):
othercolums_count.append(str(i))