Python 向窗体动态添加字段

Python 向窗体动态添加字段,python,django,django-forms,Python,Django,Django Forms,我的表单中有3个字段。 我有一个提交按钮和一个“添加额外字段”按钮。 我知道我可以在form类中使用\uuuu init\uuu方法添加字段 我对Python和Django还不熟悉,我遇到了一个初学者的问题;我的问题是: 单击“添加附加字段”按钮时,添加附加字段的过程是什么 表单是否必须再次呈现 我如何以及何时调用\uuuu init\uuuu或者我甚至必须调用它 如何将参数传递给\uuuu init\uuuu?您的表单必须基于从POST传递给它的一些变量来构造(或者盲目检查属性)。无论是否有错

我的表单中有3个字段。 我有一个提交按钮和一个“添加额外字段”按钮。 我知道我可以在form类中使用
\uuuu init\uuu
方法添加字段

我对Python和Django还不熟悉,我遇到了一个初学者的问题;我的问题是:

单击“添加附加字段”按钮时,添加附加字段的过程是什么

表单是否必须再次呈现

我如何以及何时调用
\uuuu init\uuuu
或者我甚至必须调用它


如何将参数传递给
\uuuu init\uuuu

您的表单必须基于从POST传递给它的一些变量来构造(或者盲目检查属性)。无论是否有错误,每次重新加载视图时都会构造表单本身,因此HTML需要包含关于有多少字段的信息,以构造正确数量的字段进行验证

我将以
FormSet
s的工作方式来看待这个问题:有一个隐藏字段,其中包含活动表单的数量,每个表单名称前面都有表单索引

事实上,您可以创建一个字段
FormSet

如果您不想使用
表单集
,您可以自己创建此行为

这是一个从零开始制作的-它应该给你一些想法。它还回答了您关于将参数传递给
\uuuu init\uuuu
的问题-您只需将参数传递给对象构造函数:
MyForm('arg1','arg2',kwarg1='keyword arg')

形式 看法 HTML

{{form.as_p}}
再加一个
JS

让form_count=Number($(“[name=extra_field_count]”).val();
//获取额外的表单计数,以便我们知道下一项使用什么索引。
$(“#添加另一个”)。单击(函数(){
form_count++;
let元素=$('');
元素属性('name'、'extra\u field\u'+form\u count);
$(“#表格”)。附加(元素);
//构建元素并将其附加到表单容器中
$(“[name=extra\u field\u count]”)val(form\u count);
//增加表单计数,以便我们的视图知道如何填充
//需要验证的字段太多了
})

我曾经遇到过一个案例,当时我必须使用动态字段动态创建表单。我用这个把戏做的:

from django import forms

...

dyn_form = type('DynForm',  # form name is irrelevant
                (forms.BaseForm,),
                {'base_fields': fields})
有关更多信息,请参阅此链接:

但除此之外,我还必须注入字段,即在表单类创建后动态地将字段添加到表单类中

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)

这很有效。

一种没有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
模板

{% 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 %}
{%f.fields\u dogs%}
{%trans'狗'%}{{forloop.counter}}
{%trans'名称'%}
{{field\u dog.name.errors}{{{field\u dog.name}}
{%endfor%}

此答案基于@Yuji'Tomita'Tomita的定义,并进行了一些改进和更改

尽管@Yuji'Tomita'Tomita answer很棒,并且很好地简单地说明了构建“在django表单中添加额外字段”功能所需遵循的方向,但我发现代码的某些部分存在一些问题

在这里,我提供了基于@Yuji'Tomita'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文件中)

模板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>

{{form.errors}}
{{form.non_field_errors}}
{%if errormsgs%}
{errormsgs%中的值为%0}

{{value}}

{%endfor%} {%endif%} {%表示表单\空\层中的错误。非\字段\错误%} {{error}}
{%endfor%}
{%用于表单\空\层中的字段。可见\字段%} {{field}}
{%endfor%}
添加另一个

上传

//用于保存添加字段的数量(此数量将通过视图传递到forms.py)
模板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);

})
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)
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],
        })
//检查具有此name属性的元素存在的次数:extra\u字段_*
表格_count=$('input[name*=“extra_field*”])。长度;
//单击“添加另一个”按钮后,创建一个新的输入元素
$(document.body)。在(“单击”上,“添加另一个”,函数(e){
新的_属性=$('');
//添加带有相应数字的名称属性(form_count)
新的属性attr('name','extra_field_uu'+form_count);
//在html中追加新元素
$(“#form_empty_layer”).append(新的_属性);
//递增form_count变量
form_count++;
//将表单\ u计数保存到另一个输入元素(您可以将其设置为不可见。这是您将传递给表单以创建django表单字段的内容
$(“[name=total\u input\u fields]”).val(表格计数);
})

Yuji‘Tomita’Tomita的解决方案是您能找到的最好的解决方案,但是假设您有一个多步骤表单,并且使用django formtools应用程序,您将不得不处理一些问题。谢谢Yuji‘Tomita’Tomita,您帮了我很多:)

forms.py

// 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);

})
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)
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],
        })
对于多步骤表单,您不需要额外的字段,在这段代码中,我们在第一步中使用othercolumsvalue字段

视图.py

// 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);

})
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)
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],
        })
通过覆盖获取表单()获取上下文数据()函数,可以在呈现表单之前覆盖表单。您的模板文件也不再需要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 %}

你可能想把你的问题分开。你的第一个问题被问到了