我想动态地向Django表单集添加新表单,这样当用户点击“添加”按钮时,它就会运行JavaScript,向页面添加新表单(这是表单集的一部分)。


当前回答

一种选择是创建一个包含所有可能表单的表单集,但最初将不需要的表单设置为隐藏——即display: none;。当需要显示一个表单时,将它的css显示设置为block或任何合适的显示。

如果不了解更多关于“Ajax”正在做什么的细节,就很难给出更详细的回应。

其他回答

我可以向每个正在寻找开箱即用解决方案的人推荐django-dynamic-formsets。

它取代了我从提出的解决方案中派生的所有代码,并提供了一些额外的功能,例如删除表单或对相关按钮进行风格化。

对于那些正在寻找资源以更好地理解上述解决方案的程序员:

Django动态表单

读完上面的链接后,Django文档和以前的解决方案应该更有意义了。

Django Formset文档

作为对我所困惑的内容的快速总结:管理表单包含其中表单的概述。你必须保证这些信息的准确性,这样Django才能知道你添加的表单。(社区,如果我的一些措辞不正确,请给我建议。我刚接触Django。)

因为上面所有的答案都使用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。 您还必须替换表单集的表单、类型和容器。(在上述选择的例子中)

是的,如果你有有限数量的条目,我也建议在html中渲染它们。(如果你不这样做,你将不得不使用另一个方法)。

你可以像这样隐藏它们:

{% for form in spokenLanguageFormset %}
    <fieldset class="languages-{{forloop.counter0 }} {% if spokenLanguageFormset.initial_forms|length < forloop.counter and forloop.counter != 1 %}hidden-form{% endif %}">

然后js真的很简单:

addItem: function(e){
    e.preventDefault();
    var maxForms = parseInt($(this).closest("fieldset").find("[name*='MAX_NUM_FORMS']").val(), 10);
    var initialForms = parseInt($(this).closest("fieldset").find("[name*='INITIAL_FORMS']").val(), 10);
    // check if we can add
    if (initialForms < maxForms) {
        $(this).closest("fieldset").find("fieldset:hidden").first().show();
        if ($(this).closest("fieldset").find("fieldset:visible").length == maxForms ){
            // here I'm just hiding my 'add' link
            $(this).closest(".control-group").hide();
        };
    };
}

Paolo的答案的简化版本,使用empty_form作为模板。

<h3>My Services</h3>
{{ serviceFormset.management_form }}
<div id="form_set">
    {% for form in serviceFormset.forms %}
        <table class='no_error'>
            {{ form.as_table }}
        </table>
    {% endfor %}
</div>
<input type="button" value="Add More" id="add_more">
<div id="empty_form" style="display:none">
    <table class='no_error'>
        {{ serviceFormset.empty_form.as_table }}
    </table>
</div>
<script>
    $('#add_more').click(function() {
        var form_idx = $('#id_form-TOTAL_FORMS').val();
        $('#form_set').append($('#empty_form').html().replace(/__prefix__/g, form_idx));
        $('#id_form-TOTAL_FORMS').val(parseInt(form_idx) + 1);
    });
</script>