【问题标题】:Django formset dynamic cant add formsDjango formset动态无法添加表单
【发布时间】:2019-11-27 20:40:19
【问题描述】:

我在 django 中遇到了动态表单集的问题

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

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

我认为问题出在 javascript 中,但对它了解不多我无法修复它

它的 html 图像,当我按下“+”按钮时,什么也没发生。例如,如果我将 formset 的“额外”更改为 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 %}

【问题讨论】:

标签: python django


【解决方案1】:

您可能会找到一种更简单的方法来使用 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 渲染空表单!

formset 中的表单由添加到 id 和 classes 的表单编号指定。因此,字段 cantidad 的 ID 为 id_form-0-cantidad 用于集合中的第一个表单,id_form-1-cantidad 用于第二个,依此类推。表单编号从 0 开始,因此具有 1 个表单的表单集将具有其最大前缀= 0。所有这一切都是说,TOTAL_FORMS 也等于您需要提供新表格的新前缀。然后,将 TOTAL_FORMS 增加 1 以避免验证错误。

所以,我使用.clone(true, true) 创建了一个空表单的克隆。真正的参数确保事件监听器在被克隆时被添加到元素中。如果您有表单集的事件处理程序,这很重要。没有它,克隆的元素将不会响应事件。

然后我将克隆的$newform.html() 附加到formset div,并使用正则表达式将id、名称和类中的所有__prefix__ 替换为新的表单编号:@ 987654330@.

没有尝试删除表单,但我会使用 jQuery 来完成。类似的过程 - 识别表格编号,选择父级,删除它。将 TOTAL_FORMS 减 1。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-09-21
    • 2020-07-22
    • 2014-03-24
    • 2012-08-17
    • 2016-07-17
    • 1970-01-01
    • 1970-01-01
    • 2014-02-11
    相关资源
    最近更新 更多