dynamic添加字段到表单
我有三个领域在我的forms。 我有一个提交button和一个button“添加额外的字段”。 我知道我可以在表单类中使用__init__
方法添加字段。
我是新来的Python和Django,并坚持一个初学者的问题:我的问题是:
当我点击“添加附加字段”button时,添加附加字段的过程是什么?
表单是否需要再次呈现?
我怎样和什么时候打电话__init__
或者我甚至不得不打电话?
如何将parameter passing给__init__
?
您的表单将不得不基于从您的POST传递给它的一些variables(或者盲目地检查属性)来构build。 每次重新载入视图时都会构造表单本身,所以HTML需要包含有多less字段来构造正确数量的字段进行validation。
我FormSet
的工作方式来看待这个问题:有一个隐藏的字段,其中包含活动表单的数量,每个表单名称都以表单索引作为前缀。
事实上,你可以做一个字段FormSet
https://docs.djangoproject.com/en/dev/topics/forms/formsets/#formsets
如果你不想使用FormSet
你可以自己创build这个行为。
这是从头开始 – 它应该给你一些想法。 它还回答了有关将parameter passing给__init__
– 只是将parameter passing给对象构造函数: MyForm('arg1', 'arg2', kwarg1='keyword arg')
forms
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 })
HTML
<form> <div id="forms"> {{ form.as_p }} </div> <button id="add-another">add another</button> <input type="submit" /> </form>
JS
<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>
我有一个情况,当我不得不dynamic创builddynamic字段的forms。 我是这样做的:
from django import forms ... dyn_form = type('DynForm', # form name is irrelevant (forms.BaseForm,), {'base_fields': fields})
有关更多信息,请参阅此链接: dynamic表单
但除此之外,我还必须注入字段,也就是说,一旦创build了表单类,就会dynamic添加字段。
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)
那工作。
这个答案是基于@ Yuji'Tomita'Tomita有几个改进和变化。
虽然@ Yuji'Tomita'Tomita的答案是伟大的,说明了很好的和简单的方向来build立“Django的forms添加额外的领域”的function,我发现有一些代码的一些问题。
我在这里根据@ Yuji'Tomita'Tomita的初步build议提供我的工作代码:
视图 (在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); })
没有JavaScript和字段types的方式不在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
模板
{% 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 %}
Yuji'Tomita'Tomita的解决scheme是最好的,你会发现,但假设你有一个多步骤的forms,你使用django-formtools应用程序,你将有一些问题,你将不得不照顾。 谢谢Yuji'Tomita'Tomita,你帮了我很多:)
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()函数,您可以覆盖它被呈现的forms。 您的模板文件不再需要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 %}
step2中的字段是dynamic生成的,因为名称相同,也从formtools中重新调用。 但是为了达到那里,你将不得不绕过for-each模板循环,你可以看到:
从get_context_data()函数
othercolums_count = [] for i in range(0, data_step1['othercolumsvalue']): othercolums_count.append(str(i))