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); } |
它做什么:
这个功能对我特别有帮助,因为它的设置方式允许我在整个应用程序中使用它,当我想在一个表单集中提供更多表单时,并且不需要有一个隐藏的"模板"表单来复制,只要我将表单集名称和表单的布局格式传递给它。希望它有帮助。
simplified version of using as a S paolo'答案
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); } |
一种选择是用所有可能的表单创建一个表单集,但最初将不需要的表单设置为隐藏的,即
如果不知道"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
是我也只是在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></