Django Formset动态无法添加表单

时间:2019-07-18 20:55:29

标签: python django

我在Django中遇到动态表单集问题

我正在观看本教程,我从中取出了基本代码 https://medium.com/all-about-django/adding-forms-dynamically-to-a-django-formset-375f1090c2b0

但是它不能正常工作,因为它不允许我添加更多表格

我认为问题出在javascript中,但是对此了解不多,无法解决

它的html图像,当我按下“ +”按钮时,什么也没发生。例如,如果我将formset的“ extra”更改为5,然后按“-”按钮,则我期望该工作。

forms.py

OrdenCompraModelFormset = modelformset_factory(
    OrdenCompraProducto,
    fields=[
        'tipo_producto',
        'cantidad',
        'fecha_entrega',
        'proveedor_surgerido',
    ],
    extra=1,
    widgets={
        'fecha_entrega': forms.DateInput(attrs=
                                         {'class': 'form-control', 'type': 'date'}),
    }
)

views.py

def formv_ordencompra(request):
form=OrdenCompraForm, extra=1)
    if request.method == 'GET':
        print('0')
        formset = OrdenCompraModelFormset(queryset=OrdenCompraProducto.objects.none())
    elif request.method == 'POST':
        print('1')
        formset = OrdenCompraModelFormset(request.POST)
        if formset.is_valid():
            formset_obj = formset.save(commit=False)
            ordencompra = OrdenCompra.objects.create()

            for form_obj in formset_obj:
                form_obj.orden = ordencompra
                form_obj.save()
            messages.success(request, f'La orden fue cargada!')
            return redirect('.')
    else:
        print('2')
        formset = OrdenCompraModelFormset()
    return render(request, 'webapp/formularios/ordencompra.html', {'formset': formset})

ordencompra.html

{% extends 'webapp/base.html' %}
{% load crispy_forms_tags %}
{% load static %}
{% block content_principal %}


    <div class="container-fluid">
        <h1 class="h3 mb-2 text-gray-800">Cargar "Orden de devolucion"</h1>
        <p class="mb-4">Esta orden define quien trae y cuando un recurso que este en obra</p>
        <p class="mb-4">¿Cuando cargar?: En el momento en el que se quieran traer recursos desde obra</p>

        <hr>





        <form class="form-horizontal" method="POST" action="">
            {% csrf_token %}
            {{ formset.management_form }}
            {% for form in formset %}

                <div class="row form-row spacer">
                    <div class="input-group">


                        <div class="input-group-append">
                            <button class="btn btn-success add-form-row">+</button>
                            <button class="btn btn-danger remove-form-row">-</button>
                        </div>


                        {{form|crispy}}
                    </div>
                </div>
                <hr>

            {% endfor %}

            <div class="row spacer">
                <div class="">
                    <button type="submit" class="btn btn-block btn-primary">Create</button>
                </div>
            </div>
        </form>


    </div>


{% endblock %}


{% block content_plugins %}

    <script type='text/javascript'>
        function updateElementIndex(el, prefix, ndx) {
            var id_regex = new RegExp('(' + prefix + '-\\d+)');
            var replacement = prefix + '-' + ndx;
            if ($(el).attr("for")) $(el).attr("for", $(el).attr("for").replace(id_regex, replacement));
            if (el.id) el.id = el.id.replace(id_regex, replacement);
            if (el.name) el.name = el.name.replace(id_regex, replacement);
        }
        function cloneMore(selector, prefix) {
            var newElement = $(selector).clone(true);
            var total = $('#id_' + prefix + '-TOTAL_FORMS').val();
            newElement.find(':input:not([type=button]):not([type=submit]):not([type=reset])').each(function() {
                var name = $(this).attr('name').replace('-' + (total-1) + '-', '-' + total + '-');
                var id = 'id_' + name;
                $(this).attr({'name': name, 'id': id}).val('').removeAttr('checked');
            });
            newElement.find('label').each(function() {
                var forValue = $(this).attr('for');
                if (forValue) {
                    forValue = forValue.replace('-' + (total-1) + '-', '-' + total + '-');
                    $(this).attr({'for': forValue});
                }
            });
            total++;
            $('#id_' + prefix + '-TOTAL_FORMS').val(total);
            $(selector).after(newElement);
            var conditionRow = $('.form-row:not(:last)');
            conditionRow.find('.btn.add-form-row')
                .removeClass('btn-success').addClass('btn-danger')
                .removeClass('add-form-row').addClass('remove-form-row')
                .html('<span class="glyphicon glyphicon-minus" aria-hidden="true"></span>');
            return false;
        }
        function deleteForm(prefix, btn) {
            var total = parseInt($('#id_' + prefix + '-TOTAL_FORMS').val());
            if (total > 1){
                btn.closest('.form-row').remove();
                var forms = $('.form-row');
                $('#id_' + prefix + '-TOTAL_FORMS').val(forms.length);
                for (var i=0, formCount=forms.length; i<formCount; i++) {
                    $(forms.get(i)).find(':input').each(function() {
                        updateElementIndex(this, prefix, i);
                    });
                }
            }
            return false;
        }
        $(document).on('click', '.add-form-row', function(e){
            e.preventDefault();
            cloneMore('.form-row:last', 'form');
            return false;
        });
        $(document).on('click', '.remove-form-row', function(e){
            e.preventDefault();
            deleteForm('form', $(this));
            return false;
        });
    </script>
{% endblock %}

1 个答案:

答案 0 :(得分:0)

您可能会发现一种更简单的方法来使用Django表单集中的空表单。这是一个隐藏的空表格,其中__prefix__代替了表格编号。您可以克隆此表单,然后用新的表单号替换__prefix__。这样可以减少代码。这就是我使添加表单起作用的方式。

{% load crispy_forms_tags %}

{% block content_principal %}

       {{ formset.management_form }}

       <div id="form_set">
         {% for form in formset.forms %}
              {{form.non_field_errors}}
              {{form.errors}}
              {% crispy form %}
          {% endfor %}
        </div>

        <input type="button" value="+" id="add_more"> 

        <div id="empty_form" style="display:none"> 
            {% crispy formset.empty_form %}            
        </div>

{% endblock %}

{% block content_plugins %}

  <script type="text/javascript">

    $(document).ready(function() { 

      $("#add_more").on("click", function() {         
          var form_idx = $("#id_form-TOTAL_FORMS").val();
          $newform = $("#empty_form").clone(true,true)
          $("#form_set").append($newform.html().replace(/__prefix__/g, form_idx)); 
          $("#id_form-TOTAL_FORMS").val(parseInt(form_idx)+1); 
      });

    });
</script>

{% endblock %}

为清楚起见,我仅添加了一个按钮即可添加新表格。我们正在克隆一个空表格,而不是集合中的特定表格。无需在集合中的每个表单上都具有添加按钮。您可能有理由在您的代码中执行此操作,但不是出于本示例的目的。请注意,您也可以使用Crispy渲染空白表单!

表单集中的表单由ID和类之前的表单号指定。因此,该字段的候选字段在集合中的第一个表单的ID为id_form-0-cantidad,第二个表单的ID为id_form-1-cantidad,依此类推。表单编号以0开头,因此具有1个表单的表单集将最大前缀=0。这就是说,TOTAL_FORMS也等于您需要提供新格式的新前缀。然后,将TOTAL_FORMS加1以免验证错误。

因此,我使用.clone(true, true)创建了一个空格式的副本。 true参数可确保在克隆事件侦听器时将其添加到元素中。如果您具有表单集的事件处理程序,则这一点很重要。没有它,克隆的元素将无法响应事件。

然后,我将克隆的.html()的{​​{1}}附加到formset div上,并使用正则表达式将ID,名称和类中的所有$newform替换为新的表格编号:__prefix__

还没有尝试删除表单,但是我将使用jQuery来完成。相似的过程-确定表格编号,选择父表格,将其删除。将TOTAL_FORMS减1。