我在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 %}
答案 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。