6
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Djangoでフォームの入力欄を動的に追加する

Last updated at Posted at 2020-08-21

はじめに

独学でプログラミングを学習中の大学3年生です。

以前、WebアプリケーションをPHPで作成したときにフォームの入力欄を動的に追加したことがありました。現在、WebアプリをDjangoに移行中なのですが、タイトルの問題に当たってなかなか解決しなかったためここに残しておきます。

ドキュメントにはこれしか書かれてなかった…
(2020/08/26 追記: ここにちゃんと書かれてました笑)

empty_form
BaseFormSet provides an additional attribute empty_form which returns a form instance with a prefix of prefix for easier use in dynamic forms with JavaScript.

初学者にも分かりやすいように、できるだけ簡潔なコードになるよう心がけています。

以前の記事

この記事だけで分からないときは、以前の記事を見てもらえると分かるかもしれません。

コード

Djangoでフォームを動的に扱う場合には、Formsetsのempty_formを使います。

Djangoが生成する一般的なフォームのinputには、id_form-{No.}-place_nameのようなidが自動で挿入されます。ただし、empty_formを使った場合はすべてのinputのidがid_form-__plefix__-place_nameとなるため、__plefix__の部分を数字に書き換える必要があります。

template.html
<form method="POST" id="form">
    {% csrf_token %}
    {{ form.management_form }}
    <div class="text">
        {{ form.empty_form.as_p }}
    </div>
    <button type="button" id="btn-clone">clone</button>
    <button type="submit">submit</button>
</form>

実際には、このように表示されます。

template.html
<form method="POST" id="form">
    <!-- ...... -->
    <input type="hidden" name="form-TOTAL_FORMS" value="2" id="id_form-TOTAL_FORMS">
    <!-- ...... -->
    <div class="text">
        <p>
            <label for="id_form-__prefix__-place_name">Input:</label>
            <input type="text" name="form-__prefix__-place_name" id="id_form-__prefix__-place_name">
            <input type="hidden" name="form-__prefix__-id" id="id_form-__prefix__-id">
        </p>
    </div>
    <button type="button" id="btn-clone">clone</button>
    <button type="submit">submit</button>
</form>

__prefix__を書き換えます。
また、input[id="id_form-TOTAL_FORMS"]のvalueでinputの数を指定します。

script.js
$(function() {


    // clone
    $('#btn-clone').click(function() {
        var text = $('.text').last();  // 最後尾にあるinput
        clone = text.clone().insertAfter(text);  // inputを最後尾に追加
        clone.find("input[type='text']").val('');  // valueもクローンされるので削除する
    });


    // ここからDjango用のidなどを操作する
    $('#form').submit(function() {  // フォームを送信する直前

        // フォームの入力欄の数を指定する
        const text = $('.text');
        $('[name=form-TOTAL_FORMS]').val(text.length);

        // それぞれの入力欄の__prefix__をindexで置換する
        text.each(function(index, element){
            html = $(element).html().replace(/__prefix__/g, index);
            value = $(element).find("input[type='text']").val();  // valueが消えるので保存しておく
            $(element).html(html);
            $(element).find("input[type='text']").val(value);
        });

    });
});

フォームの削除機能を実装したい場合は、以前の記事をご覧ください。ここでは見やすいようにフォームの追加だけを実装しています。なお、最終的にフォームの削除機能の実装を想定しているため、コードが冗長になっています。フォームの削除機能が必要ない場合は修正してください。

参考

6
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?