Help us understand the problem. What is going on with this article?

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

はじめに

独学でプログラミングを学習中の大学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);
        });

    });
});

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

参考

Yuji-207
就活中の大学3年生。
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした