使用Ajax动态地将表单添加到Django formset

Dynamically adding a form to a Django formset with Ajax

我想使用Ajax自动向django表单集添加新表单,这样当用户单击"添加"按钮时,它会运行javascript,向页面添加新表单(表单集的一部分)。


这就是我使用jquery的方法:

我的模板:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
My Services
{{ serviceFormset.management_form }}
{% for form in serviceFormset.forms %}
   
    <table class='no_error'>
        {{ form.as_table }}
    </table>
   
{% endfor %}
<input type="button" value="Add More" id="add_more">

    $('#add_more').click(function() {
        cloneMore('div.table:last', 'service');
    });

在javascript文件中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function cloneMore(selector, type) {
    var newElement = $(selector).clone(true);
    var total = $('#id_' + type + '-TOTAL_FORMS').val();
    newElement.find(':input').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 newFor = $(this).attr('for').replace('-' + (total-1) + '-','-' + total + '-');
        $(this).attr('for', newFor);
    });
    total++;
    $('#id_' + type + '-TOTAL_FORMS').val(total);
    $(selector).after(newElement);
}

它做什么:

cloneMore接受selector作为第一个论点,formset的type作为第二个论点。selector应该做的是传递它应该复制的内容。在本例中,我将它传递给div.table:last,以便jquery查找具有table类的最后一个表。其中的:last部分很重要,因为selector还用于确定在后面插入什么新表单。很可能你会想把它放在其他表格的末尾。type参数是为了更新management_form字段,特别是TOTAL_FORMS字段以及实际的表单字段。如果您有一个表单集,例如,Client模型,那么管理字段的ID将为id_clients-TOTAL_FORMSid_clients-INITIAL_FORMS,而表单字段的格式将为id_clients-N-fieldname,其中N是表单号,从0开始。因此,使用type参数,cloneMore函数查看当前有多少个表单,并通过新表单中的每个输入和标签,替换所有字段名/ID,从id_clients-(N)-nameid_clients-(N+1)-name等等。完成后,它更新TOTAL_FORMS字段以反映新表单并将其添加到集合的末尾。

这个功能对我特别有帮助,因为它的设置方式允许我在整个应用程序中使用它,当我想在一个表单集中提供更多表单时,并且不需要有一个隐藏的"模板"表单来复制,只要我将表单集名称和表单的布局格式传递给它。希望它有帮助。


simplified version of using as a S paolo'答案empty_form模板。P></

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
My Services
{{ serviceFormset.management_form }}

    {% for form in serviceFormset.forms %}
        <table class='no_error'>
            {{ form.as_table }}
        </table>
    {% endfor %}

<input type="button" value="Add More" id="add_more">

    <table class='no_error'>
        {{ serviceFormset.empty_form.as_table }}
    </table>


    $('#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);
    });


我刚刚发布了一个应用程序的一个片段。类似于Paolo的,但也允许删除表单。


Paolo的建议很好地起作用,但有一点需要注意:浏览器的后退/前进按钮。

如果用户使用后退/前进按钮返回到表单集,则不会呈现使用paolo脚本创建的动态元素。对一些人来说,这个问题可能会破坏交易。

例子:

1)用户使用"添加更多"按钮向表单集添加两个新表单

2)用户填写表单并提交表单集

3)用户单击浏览器中的后退按钮

4)现在表单集被缩减为原始表单,所有动态添加的表单都不存在。

这根本不是Paolo脚本的缺陷,而是DOM操作和浏览器缓存的现实。

我想可以在会话中存储表单的值,并在加载表单集以再次创建元素并从会话中重新加载值时具有一些Ajax魔力;但根据您希望如何处理同一用户和表单的多个实例,这可能会变得非常复杂。

有人对处理这件事有好的建议吗?

谢谢!


检查下面的解决方案:动态forms to DjangoP></

code.google.com http:/ / / / P - / -动态窗体集中的DjangoP></

HTTPS:/ / / / github.com javisantana Django - dinamyc形态/树/硕士/ frmP></

他们都会使用jQuery和are of Django的特异性。在第一位polished茶似乎更多是在下载和offers that which are优秀W /演示。P></


模拟和模仿:

  • 在单击"添加"按钮之前,创建一个与情况相对应的表单集。
  • 加载页面,查看源代码并记下所有字段。
  • 单击"添加"按钮(更改额外字段的数目)后,根据情况修改表单集。
  • 加载页面,查看源代码,并记下字段是如何更改的。
  • 创建一些javascript,以适当的方式修改DOM,将其从"前"状态移动到"后"状态。
  • 将该javascript附加到"添加"按钮。

虽然我知道表单集使用特殊的隐藏字段,并且大致了解脚本必须执行的操作,但我不记得最上面的细节。我上面描述的是我在你的情况下会做什么。


jQuery插件there is used for this,它与_ inline形式集恩在Django和1.3,工程perfectly prepopulation客户端侧,包括形态,增多,除尘,和_ formsets inline。P></


clonemore for another version of which,allows选择消毒领域。当你需要使用它防止被erased from several领域。P></

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
$('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);
}

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

如果不知道"ajax"正在做什么的更多细节,就很难给出更详细的响应。


我认为这是非常好的解决方案。P></

你会如何做动态窗体集中在Django?P></

does不事:克隆P></

  • 当初始形态不见L型名称
  • 把手的JavaScript for example the形态更好,- ckeditor Django
  • 保持初始日期

there is with the clonemore函数小问题。因为它也清洗the value of the generated自隐场Django Django的原因,它complain如果你试图保存窗体集中的形态与超过一空。P></

修理:here is aP></

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function cloneMore(selector, type) {
    var newElement = $(selector).clone(true);
    var total = $('#id_' + type + '-TOTAL_FORMS').val();
    newElement.find(':input').each(function() {
        var name = $(this).attr('name').replace('-' + (total-1) + '-','-' + total + '-');
        var id = 'id_' + name;

        if ($(this).attr('type') != 'hidden') {
            $(this).val('');
        }
        $(this).attr({'name': name, 'id': id}).removeAttr('checked');
    });
    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);
}


因为在上述的详细使用jQuery和让一些事在后位的wrote复合脚本:P></

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
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
    }
};

You should集第一ID和汽车_ to disable the of duplication知道虚假的ID和name。因为茶叶中的唯一输入names have to be there is done with的形态、识别和id' s not with them。You also have to replace the formtypeand the of the窗体集容器。(choicesabove the example)P></


是我也只是在recommend them the HTML渲染出来了如果你have a number of entries。(如果你不你have to user another method)。P></

你可以把他们这样:P></

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

真的那么简单:JS is theP></

1
2
3
4
5
6
7
8
9
10
11
12
13
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();
        };
    };
}

保罗bergantino @P></

to modify the handlers克隆只是附在茶行P></

1
var newElement = $(selector).clone();

forP></

1
var newElement = $(selector).clone(true);

to防止这个问题。P></


coders out there for the who are the above狩猎资源的了解,更好的解决方案:小P></

动态formsets DjangoP></

after the above the Django阅读文件和链接,让很多以前的解决方案应该更多的意识。P></

Django文档窗体集P></

作为总结,我要什么快速模式:管理混乱contains the形态概述within the forms。你必须保持准确的信息,以感知为Django to be of the forms答复你。社区政策建议,请给我(if some of My wording is off here。IM)New to Django。P></