Ajax를 사용하여 Django 폼셋에 새 폼을 자동으로 추가하고 싶습니다. 따라서 사용자가 "add"버튼을 클릭하면 새로운 폼 (폼셋의 일부)을 페이지에 추가하는 JavaScript가 실행됩니다.
Ajax를 사용하여 Django 폼셋에 새 폼을 자동으로 추가하고 싶습니다. 따라서 사용자가 "add"버튼을 클릭하면 새로운 폼 (폼셋의 일부)을 페이지에 추가하는 JavaScript가 실행됩니다.
답변:
이것이 jQuery를 사용하여 수행하는 방법입니다 .
내 템플릿 :
<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>
자바 스크립트 파일에서 :
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
첫 번째 인수로하고, type
제 2 하나의 formset의. 무엇을 selector
해야하는 것은 중복해야하는지 전달할 수 있습니다. 이 경우 div.table:last
jQuery가 클래스가있는 마지막 테이블을 찾도록 전달합니다 table
. :last
이 있기 때문에 그 부분은 중요하다 selector
또한 새로운 형태 다음에 삽입 할 것을 결정하는 데 사용됩니다. 나머지 양식의 끝에 그것을 원할 것입니다. type
우리는 업데이트 할 수 있도록 인수는 management_form
특히 필드 TOTAL_FORMS
뿐만 아니라 실제 양식 필드를. 당신은, 말하자면, 가득 해당 formset있는 경우 Client
모델을 관리 필드의 ID를해야합니다 id_clients-TOTAL_FORMS
및 id_clients-INITIAL_FORMS
양식 필드의 형식이됩니다 반면, id_clients-N-fieldname
과N
로 시작하는 양식 번호입니다 0
. 그래서 함께 type
인수로 사용 cloneMore
하는 방법을 여러 가지 형태의 기능 모습이 현재와 같은에서 모든 필드 이름 / ID를 대체하는 새로운 형태의 내부의 모든 입력 및 레이블을 통과 id_clients-(N)-name
하는 id_clients-(N+1)-name
등등. 완료된 후에는 TOTAL_FORMS
필드를 업데이트하여 새 양식을 반영하고 세트 끝에 추가합니다.
이 기능은 설정 방식에 따라 양식 세트에서 더 많은 양식을 제공하려고 할 때 앱 전체에서 사용할 수 있으며 복제하기 위해 숨겨진 "템플릿"양식을 가질 필요가 없기 때문에 특히 유용합니다. 내가 그것을 전달하는 한 양식 이름과 양식이 배치되는 형식. 도움이 되길 바랍니다.
prefix
에서 Formset Object 의 멤버에 값을 할당해야한다는 것을 알았습니다 . 이것은 함수 의 type
인수 와 같은 값이어야합니다 cloneMore
.
empty_form
템플릿으로 사용하는 Paolo의 답변 단순화 버전 .
<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)
깨끗한 방법으로 한 가지 양식 만 가져옵니다. 이 문제를 처리하는 방법을 설명해 주시겠습니까?
empty_form
잘 활용합니다.
나는 한 코드 조각 게시 한 동안의 뒷면에 근무 응용 프로그램에서합니다. Paolo와 유사하지만 양식을 삭제할 수도 있습니다.
Paolo의 제안은 브라우저의 뒤로 / 앞으로 버튼이라는 하나의 경고와 함께 아름답게 작동합니다.
사용자가 뒤로 / 앞으로 단추를 사용하여 양식 세트로 돌아 오면 Paolo의 스크립트로 작성된 동적 요소가 렌더링되지 않습니다. 일부에게는 문제가 될 수있는 문제입니다.
예:
1) 사용자는 "추가 항목"버튼을 사용하여 두 개의 새 양식을 양식 세트에 추가합니다.
2) 사용자가 양식을 채우고 양식을 제출합니다.
3) 사용자가 브라우저에서 뒤로 버튼을 클릭
4) 이제 양식 세트가 원래 양식으로 축소되고 동적으로 추가 된 모든 양식이 없습니다
이것은 Paolo의 스크립트에 전혀 결함이 아닙니다. 그러나 DOM 조작과 브라우저 캐시의 삶의 사실.
양식에 값을 세션에 저장할 수 있고 양식 세트가 요소를 다시 작성하고 세션에서 값을 다시로드하기 위해로드 할 때 약간의 마법을 가질 수 있다고 가정합니다. 그러나 동일한 사용자와 양식의 여러 인스턴스에 대해 항문이 얼마나 필요한지에 따라 이것은 매우 복잡해질 수 있습니다.
누구든지 이것을 다루는 데 좋은 제안이 있습니까?
감사!
동적 장고 양식에 대한 다음 솔루션을 확인하십시오.
http://code.google.com/p/django-dynamic-formset/
https://github.com/javisantana/django-dinamyc-form/tree/master/frm
둘 다 jQuery를 사용하고 장고에 따라 다릅니다. 첫 번째는 좀 더 세련되어 보이고 우수한 데모가 포함 된 다운로드를 제공합니다.
시뮬레이션 및 모방 :
<input>
필드를 기록하십시오.<input>
필드 변경 .폼 세트가 특수 숨겨진 <input>
필드를 사용 하고 대본이 무엇을 해야하는지 아는 동안 머리 꼭대기에서 세부 사항을 기억하지는 않습니다. 위에서 설명한 것은 귀하의 상황에서 내가 할 일입니다.
이것에 대한 jquery 플러그인이 있으며, 장고 1.3에서 inline_form 세트와 함께 사용했으며 사전 채우기, 클라이언트 측 양식 추가, 제거 및 여러 inline_formsets를 포함하여 완벽하게 작동합니다.
필드를 선택적으로 살균 할 수있는 또 다른 cloneMore 버전. 여러 필드가 지워지지 않도록해야 할 때 사용하십시오.
$('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 함수에는 약간의 문제가 있습니다. 또한 django 자동 생성 된 숨겨진 필드의 값을 정리하기 때문에 하나 이상의 빈 양식으로 양식 세트를 저장하려고하면 django가 불평하게됩니다.
다음은 수정 사항입니다.
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);
}
나는 이것이 훨씬 더 나은 해결책이라고 생각합니다.
복제품이하지 않는 것 :
위의 솔루션을 조금 더 잘 이해하기 위해 사냥 자원을 가지고있는 코더에게는
위의 링크를 읽은 후 Django 설명서와 이전 솔루션이 훨씬 더 합리적입니다.
내가 혼란스러워 한 것에 대한 간단한 요약으로 : 관리 양식에는 양식의 개요가 포함되어 있습니다. Django가 추가 한 양식을 인식하려면 해당 정보를 정확하게 유지해야합니다. (커뮤니티, 내 문구 중 일부가 여기에 없으면 제안 해주십시오. Django를 처음 사용합니다.)
또한 제한된 수의 항목이있는 경우 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();
};
};
}
위의 모든 답변은 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가 아닌 입력으로 수행됩니다. 또한 교체해야 할 form
, type
그리고 해당 formset의 컨테이너입니다. (위의 예에서 choices
)