Kullanıcı bir "Ekle" düğmesini tıklattığında sayfaya yeni bir form (form kümesinin bir parçası olan) ekleyen JavaScript çalıştırır böylece otomatik olarak Ajax kullanarak bir Django form kümesine yeni formlar eklemek istiyorum.
Kullanıcı bir "Ekle" düğmesini tıklattığında sayfaya yeni bir form (form kümesinin bir parçası olan) ekleyen JavaScript çalıştırır böylece otomatik olarak Ajax kullanarak bir Django form kümesine yeni formlar eklemek istiyorum.
Yanıtlar:
Bu nasıl jQuery kullanarak yapmak :
Şablonum:
<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>
Bir javascript dosyasında:
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);
}
Bu ne yapar:
cloneMore
selector
ilk argüman, type
formset ise 2. argüman olarak kabul edilir . Ne selector
yapmalıyım onu çoğaltmak gereken geçmek olduğunu. Bu durumda, ben div.table:last
jQuery bir sınıf ile son tablo arar böylece onu geçmek table
. Bunun bir :last
kısmı önemlidir, çünkü selector
yeni formun sonradan ne ekleneceğini belirlemek için de kullanılır. Muhtemelen formların geri kalanının sonunda olmasını istersiniz. type
Biz güncelleme böylece argümanı management_form
özellikle alanını TOTAL_FORMS
, hem de fiili form alanları. Eğer, diyelim ki, tam bir formset varsa Client
modellerinde, yönetim alanları arasında kimliğe sahip olur id_clients-TOTAL_FORMS
ve id_clients-INITIAL_FORMS
form alanları bir biçimde olacaktır ederken, id_clients-N-fieldname
ileN
ile başlayan form numarasıdır 0
. Yani birlikte type
argüman cloneMore
kaç formları de fonksiyon görünüyor orada şu anda ve böyle bir şey gelen tüm alan adları / kimlikleri yerine yeni bir form içindeki her giriş ve etiket geçer id_clients-(N)-name
etmek id_clients-(N+1)-name
vb. Tamamlandıktan sonra, TOTAL_FORMS
alanı yeni formu yansıtacak şekilde günceller ve kümenin sonuna ekler.
Bu işlev benim için özellikle yararlıdır çünkü kurulum şekli, bir form kümesinde daha fazla form sağlamak istediğimde uygulama boyunca kullanmamı sağlar ve çoğaltmak için gizli bir "şablon" formuna sahip olmamı gerektirmez form seti adını ve formların düzenlendiği biçimi geçirdiğim sürece. Umarım yardımcı olur.
prefix
Formset Nesnesi üyesine bir değer atamanız gerektiğini buldum . Bu type
, cloneMore
işlevin bağımsız değişkeniyle aynı değerde olmalıdır .
Paolo'nun cevabını empty_form
şablon olarak kullanarak basitleştirilmiş versiyonu .
<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)
temiz bir yöntemle, sadece bir form olsun. Bunu görüşlerle nasıl başa çıkacağınızı açıklar mısınız?
empty_form
Ben takdir mevcut Django yardımcıları (gibi ), mükemmel kullanır .
Ben ettik pasajı yayınlanmıştır bir süre sırtında çalışmış bir uygulamadan. Paolo'nunkine benzer, ancak formları silmenize de izin verir.
Paolo'nun önerisi, tarayıcının geri / ileri düğmeleri olan bir uyarı ile güzel çalışıyor.
Kullanıcı geri / ileri düğmesini kullanarak form kümesine dönerse, Paolo'nun komut dosyasıyla oluşturulan dinamik öğeler oluşturulmaz. Bazıları için bir anlaşma kırıcı olabilecek bir konu.
Misal:
1) Kullanıcı "eklenti" düğmesini kullanarak form kümesine iki yeni form ekler
2) Kullanıcı formları doldurur ve form setini gönderir
3) Kullanıcı tarayıcıdaki geri düğmesini tıklar
4) Formset artık orijinal forma indirgenmiştir, dinamik olarak eklenen tüm formlar orada değildir
Bu Paolo'nun senaryosunda bir kusur değil; ancak dom manipülasyonu ve tarayıcının önbelleği ile hayatın bir gerçeği.
Ben bir oturumda formun değerlerini saklamak ve yeniden öğeleri oluşturmak ve oturum değerleri yeniden yüklemek için form kümesi yüklendiğinde bazı ajax büyü var varsayalım; ancak aynı kullanıcı ve formun birden çok örneği hakkında ne kadar anal olmak istediğinize bağlı olarak bu çok karmaşık hale gelebilir.
Herkes bununla başa çıkmak için iyi bir öneri var mı?
Teşekkürler!
Dinamik django formlarına aşağıdaki çözümlere göz atın:
http://code.google.com/p/django-dynamic-formset/
https://github.com/javisantana/django-dinamyc-form/tree/master/frm
Her ikisi de jQuery'den yararlanır ve django'ya özgüdür. İlk biraz daha parlak görünüyor ve mükemmel w / demolar gelen bir indirme sunuyor.
Simüle edin ve taklit edin:
<input>
alanları .<input>
alanların değiştiğini .Formetlerin özel gizli <input>
alanları kullandığını ve betiğin ne yapması gerektiğini bildiğim halde, başımın üstündeki ayrıntıları hatırlamıyorum. Yukarıda tarif ettiğim, sizin durumunuzda ne yapacağım.
Bunun için bir jquery eklentisi var, Django 1.3'te ayarlanmış inline_form ile kullandım ve ön popülasyon, istemci tarafı form ekleme, kaldırma ve çoklu inline_formsets dahil olmak üzere mükemmel çalışıyor.
Bir seçenek, olası her formla bir form kümesi oluşturmak, ancak başlangıçta gerekli olmayan formları gizli olarak ayarlamaktır display: none;
. Bir form görüntülemek gerektiğinde, css ekranınıblock
veya uygun olanı seçin.
"Ajax" cihazınızın ne yaptığına dair daha fazla ayrıntı bilmeden, daha ayrıntılı bir yanıt vermek zor.
Alanların seçici olarak dezenfekte edilmesine izin veren daha fazla versiyon. Birkaç alanın silinmesini önlemek istediğinizde kullanın.
$('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);
}
CloneMore işleviyle ilgili küçük bir sorun var. Ayrıca, otomatik olarak oluşturulan django gizli alanlarının değerini de temizlediğinden, bir form kümesini birden fazla boş formla kaydetmeye çalıştığınızda django'nun şikayet etmesine neden olur.
İşte bir düzeltme:
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);
}
Bence bu çok daha iyi bir çözüm.
Django'da nasıl dinamik bir form seti yaparsınız?
Klonlama yapmaz:
Yukarıdaki çözümleri biraz daha iyi anlamak için kaynakları avlayan kodlayıcılar için:
Yukarıdaki bağlantıyı okuduktan sonra, Django belgeleri ve önceki çözümler çok daha mantıklı olmalıdır.
Ne tarafından karıştırıldığını hızlı bir özet olarak: Yönetim Formu içinde formların bir genel bakış içerir. Django'nun eklediğiniz formlardan haberdar olması için bu bilgileri doğru tutmalısınız. (Topluluk, bazı ifadelerim burada değilse lütfen bana önerilerde bulunun. Django'da yeniyim.)
@Paolo Bergantino
bağlı tüm işleyicileri klonlamak için sadece satır değiştirmek
var newElement = $(selector).clone();
için
var newElement = $(selector).clone(true);
Ayrıca, sınırlı sayıda girişiniz varsa, bunları html'de oluşturmanızı da öneririm. (Yapmazsanız başka bir yöntem kullanmanız gerekir).
Bunları şu şekilde saklayabilirsiniz:
{% for form in spokenLanguageFormset %}
<fieldset class="languages-{{forloop.counter0 }} {% if spokenLanguageFormset.initial_forms|length < forloop.counter and forloop.counter != 1 %}hidden-form{% endif %}">
O zaman js gerçekten basit:
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();
};
};
}
Yukarıdaki tüm cevaplar jQuery kullanmak ve bazı şeyler biraz karmaşık yapmak için aşağıdaki komut yazdı:
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
}
};
Öncelikle auto_id öğesini false olarak ayarlamalı ve böylece kimlik ve adın çoğaltılmasını devre dışı bırakmalısınız. Girdi adları orada formda benzersiz olması gerektiğinden, tüm kimlikler id'lerle değil onlarla yapılır. Ayrıca değiştirmek zorunda form
, type
ve formset kabı. (Yukarıdaki örnekte choices
)