我想动态地向Django表单集添加新表单,这样当用户点击“添加”按钮时,它就会运行JavaScript,向页面添加新表单(这是表单集的一部分)。
当前回答
因为上面所有的答案都使用jQuery,使一些事情有点复杂,我写了下面的脚本:
function $(selector, element) {
if (!element) {
element = document
}
return element.querySelector(selector)
}
function $$(selector, element) {
if (!element) {
element = document
}
return element.querySelectorAll(selector)
}
function hasReachedMaxNum(type, form) {
var total = parseInt(form.elements[type + "-TOTAL_FORMS"].value);
var max = parseInt(form.elements[type + "-MAX_NUM_FORMS"].value);
return total >= max
}
function cloneMore(element, type, form) {
var totalElement = form.elements[type + "-TOTAL_FORMS"];
total = parseInt(totalElement.value);
newElement = element.cloneNode(true);
for (var input of $$("input", newElement)) {
input.name = input.name.replace("-" + (total - 1) + "-", "-" + total + "-");
input.value = null
}
total++;
element.parentNode.insertBefore(newElement, element.nextSibling);
totalElement.value = total;
return newElement
}
var addChoiceButton = $("#add-choice");
addChoiceButton.onclick = function() {
var choices = $("#choices");
var createForm = $("#create");
cloneMore(choices.lastElementChild, "choice_set", createForm);
if (hasReachedMaxNum("choice_set", createForm)) {
this.disabled = true
}
};
首先,您应该将auto_id设置为false,从而禁用id和name的复制。因为输入名称在表单中必须是唯一的,所以所有的标识都是用它们来完成的,而不是用id。 您还必须替换表单集的表单、类型和容器。(在上述选择的例子中)
其他回答
因为上面所有的答案都使用jQuery,使一些事情有点复杂,我写了下面的脚本:
function $(selector, element) {
if (!element) {
element = document
}
return element.querySelector(selector)
}
function $$(selector, element) {
if (!element) {
element = document
}
return element.querySelectorAll(selector)
}
function hasReachedMaxNum(type, form) {
var total = parseInt(form.elements[type + "-TOTAL_FORMS"].value);
var max = parseInt(form.elements[type + "-MAX_NUM_FORMS"].value);
return total >= max
}
function cloneMore(element, type, form) {
var totalElement = form.elements[type + "-TOTAL_FORMS"];
total = parseInt(totalElement.value);
newElement = element.cloneNode(true);
for (var input of $$("input", newElement)) {
input.name = input.name.replace("-" + (total - 1) + "-", "-" + total + "-");
input.value = null
}
total++;
element.parentNode.insertBefore(newElement, element.nextSibling);
totalElement.value = total;
return newElement
}
var addChoiceButton = $("#add-choice");
addChoiceButton.onclick = function() {
var choices = $("#choices");
var createForm = $("#create");
cloneMore(choices.lastElementChild, "choice_set", createForm);
if (hasReachedMaxNum("choice_set", createForm)) {
this.disabled = true
}
};
首先,您应该将auto_id设置为false,从而禁用id和name的复制。因为输入名称在表单中必须是唯一的,所以所有的标识都是用它们来完成的,而不是用id。 您还必须替换表单集的表单、类型和容器。(在上述选择的例子中)
另一个cloneMore版本,允许对字段进行选择性消毒。当您需要防止多个字段被擦除时使用它。
$('table tr.add-row a').click(function() {
toSanitize = new Array('id', 'product', 'price', 'type', 'valid_from', 'valid_until');
cloneMore('div.formtable table tr.form-row:last', 'form', toSanitize);
});
function cloneMore(selector, type, sanitize) {
var newElement = $(selector).clone(true);
var total = $('#id_' + type + '-TOTAL_FORMS').val();
newElement.find(':input').each(function() {
var namePure = $(this).attr('name').replace(type + '-' + (total-1) + '-', '');
var name = $(this).attr('name').replace('-' + (total-1) + '-','-' + total + '-');
var id = 'id_' + name;
$(this).attr({'name': name, 'id': id}).removeAttr('checked');
if ($.inArray(namePure, sanitize) != -1) {
$(this).val('');
}
});
newElement.find('label').each(function() {
var newFor = $(this).attr('for').replace('-' + (total-1) + '-','-' + total + '-');
$(this).attr('for', newFor);
});
total++;
$('#id_' + type + '-TOTAL_FORMS').val(total);
$(selector).after(newElement);
}
我可以向每个正在寻找开箱即用解决方案的人推荐django-dynamic-formsets。
它取代了我从提出的解决方案中派生的所有代码,并提供了一些额外的功能,例如删除表单或对相关按钮进行风格化。
对于那些正在寻找资源以更好地理解上述解决方案的程序员:
Django动态表单
读完上面的链接后,Django文档和以前的解决方案应该更有意义了。
Django Formset文档
作为对我所困惑的内容的快速总结:管理表单包含其中表单的概述。你必须保证这些信息的准确性,这样Django才能知道你添加的表单。(社区,如果我的一些措辞不正确,请给我建议。我刚接触Django。)
Paolo的建议很好用,但有一点需要注意——浏览器的后退/前进按钮。
如果用户使用后退/前进按钮返回到表单集,那么用Paolo的脚本创建的动态元素将不会被呈现。对一些人来说,这个问题可能是交易的破坏者。
例子:
1)用户使用“add-more”按钮向表单集中添加两个新表单
2)用户填充表单并提交表单集
3)用户在浏览器中点击后退按钮
4)形式集现在减少到原来的形式,所有动态添加的形式不存在
这根本不是保罗剧本的缺陷;但是dom操作和浏览器缓存是一个现实。
我认为可以在会话中存储表单的值,并在表单集加载时再次创建元素并从会话中重新加载值;但这取决于你想要的相同用户和表单的多个实例,这可能会变得非常复杂。
有人有好的建议吗?
谢谢!
推荐文章
- SQLAlchemy是否有与Django的get_or_create等价的函数?
- 如何选择一个记录和更新它,与一个单一的查询集在Django?
- Django REST框架:向ModelSerializer添加额外字段
- 如何在django上自动化createsuperuser ?
- 如何将Django QuerySet转换为列表?
- 如何直接从测试驱动程序调用自定义的Django manage.py命令?
- 在Python Django中运行单元测试时,如何禁用日志记录?
- Django CSRF检查Ajax POST请求失败
- 如何重命名一个模型字段使用南方?
- Django修复Admin复数
- 如何在Django上列出urlpatterns(端点)?
- Django自引用外键
- Django:“projects”vs“apps”
- 我如何在Django中过滤一个DateTimeField的日期?
- 如何在Django中序列化一个模型实例?