Programing

Ajax를 사용하여 Django 폼셋에 폼을 동적으로 추가

lottogame 2020. 4. 4. 10:11
반응형

Ajax를 사용하여 Django 폼셋에 폼을 동적으로 추가


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);
}

그것이하는 일 :

cloneMoreselector첫 번째 인수로, typeformset의 두 번째 인수로 받아들 입니다. 무엇을 selector해야하는 것은 중복해야하는지 전달할 수 있습니다. 이 경우 div.table:lastjQuery가 클래스가있는 마지막 테이블을 찾도록 전달합니다 table. :last이 있기 때문에 그 부분은 중요하다 selector또한 새로운 형태 다음에 삽입 할 것을 결정하는 데 사용됩니다. 나머지 양식의 끝에 그것을 원할 것입니다. type우리는 업데이트 할 수 있도록 인수는 management_form특히 필드 TOTAL_FORMS뿐만 아니라 실제 양식 필드를. 당신은, 말하자면, 가득 해당 formset있는 경우 Client모델을 관리 필드의 ID를해야합니다 id_clients-TOTAL_FORMSid_clients-INITIAL_FORMS양식 필드의 형식이됩니다 반면, id_clients-N-fieldnameN로 시작하는 양식 번호입니다 0. 그래서 함께 type인수로 사용 cloneMore하는 방법을 여러 가지 형태의 기능 모습이 현재와 같은에서 모든 필드 이름 / ID를 대체하는 새로운 형태의 내부의 모든 입력 및 레이블을 통과 id_clients-(N)-name하는 id_clients-(N+1)-name등등. 완료된 후에는 TOTAL_FORMS필드를 업데이트하여 새 양식을 반영하고 세트 끝에 추가합니다.

이 기능은 설정 방식에 따라 양식 세트에서 더 많은 양식을 제공하려고 할 때 앱 전체에서 사용할 수 있으며 복제하기 위해 숨겨진 "템플릿"양식을 가질 필요가 없기 때문에 특히 유용합니다. 내가 그것을 전달하는 한 양식 이름과 양식이 배치되는 형식. 도움이 되길 바랍니다.


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>

나는 한 코드 조각 게시 한 동안의 뒷면에 근무 응용 프로그램에서합니다. 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>필드 가 어떻게 변경 되었는지 메모하십시오 .
  • DOM을 이전 상태에서 이후 상태 로 이동하기에 적합한 방식으로 DOM을 수정하는 JavaScript를 작성하십시오 .
  • 해당 JavaScript를 "추가"버튼에 첨부하십시오.

폼 세트가 특수 숨겨진 <input>필드를 사용 하고 대본이 무엇을 해야하는지 아는 동안 머리 꼭대기에서 세부 사항을 기억하지는 않습니다. 위에서 설명한 것은 귀하의 상황에서 내가 할 일입니다.


이것에 대한 jquery 플러그인이 있으며, 장고 1.3에서 inline_form 세트와 함께 사용했으며 사전 채우기, 클라이언트 측 추가, 제거 및 여러 inline_formsets를 포함하여 완벽하게 작동합니다.


하나의 옵션은 가능한 모든 양식으로 양식 세트를 작성하지만 처음에는 필요하지 않은 양식을 숨김으로 설정하는 것 display: none;입니다. 양식을 표시해야 할 경우 CSS 표시 block또는 적절한 것으로 설정하십시오.

"Ajax"가 수행하는 작업에 대한 자세한 내용을 알지 못하면 더 자세한 응답을하기가 어렵습니다.


필드를 선택적으로 살균 할 수있는 또 다른 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-ckeditor와 같은 형태로 Javascript를 더 잘 처리합니다.
  • 초기 데이터 유지

@ 파올로 베르 간 티노

연결된 모든 핸들러를 복제하려면 라인을 수정하십시오.

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

...에 대한

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

이 문제 를 방지하기 위해 .


또한 제한된 수의 항목이있는 경우 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)


위의 솔루션을 조금 더 잘 이해하기 위해 사냥 자원을 가지고있는 코더에게는

장고 다이내믹 폼셋

위의 링크를 읽은 후 Django 설명서와 이전 솔루션이 훨씬 더 합리적입니다.

장고 폼셋 문서

내가 혼란스러워했던 것에 대한 간단한 요약으로 : 관리 양식에는 양식에 대한 개요가 들어 있습니다. Django가 추가 한 양식을 인식하려면 해당 정보를 정확하게 유지해야합니다. (커뮤니티, 내 문구 중 일부가 여기에 없으면 제안을 보내주세요. 장고를 처음 사용합니다.)

참고 URL : https://stackoverflow.com/questions/501719/dynamically-adding-a-form-to-a-django-formset-with-ajax

반응형