Tôi muốn tự động thêm các biểu mẫu mới vào bộ định dạng Django bằng Ajax, để khi người dùng nhấp vào nút "thêm", nó sẽ chạy JavaScript để thêm một biểu mẫu mới (là một phần của bộ định dạng) vào trang.
Tôi muốn tự động thêm các biểu mẫu mới vào bộ định dạng Django bằng Ajax, để khi người dùng nhấp vào nút "thêm", nó sẽ chạy JavaScript để thêm một biểu mẫu mới (là một phần của bộ định dạng) vào trang.
Câu trả lời:
Đây là cách tôi làm, sử dụng jQuery :
Mẫu của tôi:
<h3>My Services</h3>
{{ serviceFormset.management_form }}
{% for form in serviceFormset.forms %}
<div class='table'>
<table class='no_error'>
{{ form.as_table }}
</table>
</div>
{% endfor %}
<input type="button" value="Add More" id="add_more">
<script>
$('#add_more').click(function() {
cloneMore('div.table:last', 'service');
});
</script>
Trong tệp javascript:
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);
}
Những gì nó làm:
cloneMore
chấp nhận selector
làm đối số đầu tiên và định type
dạng là đối số thứ hai. Những gì selector
nên làm là vượt qua nó những gì nó nên nhân đôi. Trong trường hợp này, tôi chuyển nó div.table:last
để jQuery tìm bảng cuối cùng với một lớp table
. Một :last
phần của nó rất quan trọng vì selector
cũng được sử dụng để xác định hình thức mới sẽ được chèn vào sau. Nhiều khả năng bạn muốn nó ở phần còn lại của các biểu mẫu. Đối type
số là để chúng ta có thể cập nhật management_form
trường, đáng chú ý TOTAL_FORMS
, cũng như các trường mẫu thực tế. Nếu bạn có một bộ định dạng đầy đủ, giả sử, Client
các mô hình, các trường quản lý sẽ có ID của id_clients-TOTAL_FORMS
và id_clients-INITIAL_FORMS
, trong khi các trường của biểu mẫu sẽ có định dạng id_clients-N-fieldname
vớiN
là số mẫu, bắt đầu bằng 0
. Vì vậy, với những type
lập luận cloneMore
ngoại hình chức năng có bao nhiêu hình thức hiện có, và đi qua tất cả các đầu vào và nhãn bên trong hình thức mới thay thế tất cả các tên trường / id từ một cái gì đó như id_clients-(N)-name
để id_clients-(N+1)-name
và vân vân. Sau khi hoàn thành, nó cập nhật TOTAL_FORMS
trường để phản ánh biểu mẫu mới và thêm nó vào cuối tập hợp.
Chức năng này đặc biệt hữu ích với tôi vì cách thiết lập nó cho phép tôi sử dụng nó trong suốt ứng dụng khi tôi muốn cung cấp nhiều biểu mẫu hơn trong một biểu mẫu và không khiến tôi phải có một biểu mẫu "mẫu" ẩn để sao chép miễn là tôi chuyển cho nó tên tập tin định dạng và định dạng mà các biểu mẫu được đặt ra. Hy vọng nó giúp.
prefix
thành viên của Đối tượng định dạng. Điều này sẽ có cùng giá trị như type
đối số cho cloneMore
hàm.
Phiên bản đơn giản hóa câu trả lời của Paolo sử dụng empty_form
làm mẫu.
<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>
CompetitorFormSet = modelformset_factory(ProjectCompetitor, formset=CompetitorFormSets)
ctx['competitor_form_set'] = CompetitorFormSet(request.POST)
tôi chỉ nhận được một hình thức, trong phương thức sạch. bạn có thể giải thích làm thế nào để giải quyết điều này trong quan điểm?
empty_form
), mà tôi đánh giá cao.
Tôi đã đăng một đoạn trích từ một ứng dụng mà tôi đã làm việc một thời gian trước. Tương tự như của Paolo, nhưng cũng cho phép bạn xóa các biểu mẫu.
Gợi ý của Paolo hoạt động rất đẹp với một cảnh báo - các nút quay lại / chuyển tiếp của trình duyệt.
Các phần tử động được tạo bằng tập lệnh của Paolo sẽ không được hiển thị nếu người dùng quay lại bộ định dạng bằng nút quay lại / chuyển tiếp. Một vấn đề có thể là một phá vỡ thỏa thuận cho một số.
Thí dụ:
1) Người dùng thêm hai biểu mẫu mới vào bộ định dạng bằng nút "thêm"
2) Người dùng điền vào biểu mẫu và gửi biểu mẫu
3) Người dùng nhấp vào nút quay lại trong trình duyệt
4) Formset hiện được giảm xuống dạng ban đầu, tất cả các dạng được thêm động đều không có
Đây hoàn toàn không phải là khiếm khuyết với kịch bản của Paolo; nhưng thực tế cuộc sống với thao tác dom và bộ nhớ cache của trình duyệt.
Tôi cho rằng người ta có thể lưu trữ các giá trị của biểu mẫu trong phiên và có một số phép thuật ajax khi bộ định dạng tải để tạo lại các phần tử và tải lại các giá trị từ phiên; nhưng tùy thuộc vào mức độ hậu môn mà bạn muốn về cùng một người dùng và nhiều trường hợp của biểu mẫu này có thể trở nên rất phức tạp.
Bất cứ ai có một đề nghị tốt để đối phó với điều này?
Cảm ơn!
Kiểm tra các giải pháp sau đây cho các hình thức django năng động:
http://code.google.com.vn/p/django-dynamic-formset/
https://github.com/javisantana/django-dinamyc-form/tree/master/frm
Cả hai đều sử dụng jQuery và đặc trưng cho django. Cái đầu tiên có vẻ bóng bẩy hơn một chút và cung cấp một bản tải xuống đi kèm với những bản demo tuyệt vời.
Mô phỏng và bắt chước:
<input>
trường.<input>
trường thay đổi.Mặc dù tôi biết các bộ định dạng sử dụng các <input>
trường ẩn đặc biệt và biết khoảng kịch bản phải làm gì, tôi không nhớ các chi tiết ngoài đỉnh đầu. Những gì tôi mô tả ở trên là những gì tôi sẽ làm trong tình huống của bạn.
Có một plugin jquery cho cái này , tôi đã sử dụng nó với inline_form được thiết lập trong Django 1.3 và nó hoạt động hoàn hảo, bao gồm cả tiền xử lý, thêm biểu mẫu phía máy khách, xóa và nhiều inline_formsets.
Một tùy chọn sẽ là tạo một bộ định dạng với mọi hình thức có thể, nhưng ban đầu đặt các biểu mẫu không yêu cầu thành ẩn - tức là , display: none;
. Khi cần hiển thị biểu mẫu, hãy đặt màn hình css thànhblock
hoặc bất cứ điều gì phù hợp.
Không biết thêm chi tiết về "Ajax" của bạn đang làm gì, thật khó để đưa ra phản hồi chi tiết hơn.
Một phiên bản cloneMore khác, cho phép vệ sinh chọn lọc các trường. Sử dụng nó khi bạn cần ngăn một số trường bị xóa.
$('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);
}
Có một vấn đề nhỏ với chức năng cloneMore. Vì nó cũng làm sạch giá trị của các trường ẩn được tạo tự động django, nó khiến django phàn nàn nếu bạn cố lưu một bộ định dạng có nhiều hơn một dạng trống.
Đây là một sửa chữa:
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);
}
Tôi nghĩ rằng đây là một giải pháp tốt hơn nhiều.
Làm thế nào bạn có thể tạo một bộ định dạng động trong Django?
Có phải mọi thứ nhân bản không:
Đối với các lập trình viên ngoài kia đang săn lùng tài nguyên để hiểu các giải pháp trên tốt hơn một chút:
Sau khi đọc liên kết trên, tài liệu Django và các giải pháp trước đó sẽ có ý nghĩa hơn rất nhiều.
Như một bản tóm tắt nhanh chóng về những gì tôi đã bị nhầm lẫn bởi: Biểu mẫu quản lý chứa tổng quan về các biểu mẫu bên trong. Bạn phải giữ thông tin đó chính xác để Django nhận thức được các biểu mẫu bạn thêm. (Cộng đồng, xin vui lòng cho tôi đề xuất nếu một số từ ngữ của tôi bị tắt ở đây. Tôi mới biết về Django.)
@Paolo Bergantino
để sao chép tất cả các trình xử lý đính kèm chỉ cần sửa đổi dòng
var newElement = $(selector).clone();
cho
var newElement = $(selector).clone(true);
để ngăn chặn vấn đề này.
Yea tôi cũng khuyên bạn chỉ nên hiển thị chúng trong html nếu bạn có số lượng mục nhập hữu hạn. (Nếu bạn không phải sử dụng phương pháp khác).
Bạn có thể ẩn chúng như thế này:
{% for form in spokenLanguageFormset %}
<fieldset class="languages-{{forloop.counter0 }} {% if spokenLanguageFormset.initial_forms|length < forloop.counter and forloop.counter != 1 %}hidden-form{% endif %}">
Sau đó, js thực sự đơn giản:
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();
};
};
}
Bởi vì tất cả các câu trả lời ở trên đều sử dụng jQuery và làm cho một số thứ hơi phức tạp, tôi đã viết đoạn script sau:
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
}
};
Trước tiên, bạn nên đặt auto_id thành false và do đó vô hiệu hóa sao chép id và tên. Bởi vì tên đầu vào phải là duy nhất ở dạng đó, tất cả các nhận dạng được thực hiện với chúng chứ không phải với id. Bạn cũng phải thay thế form
, type
và container của formset. (Trong ví dụ trên choices
)