[PYTHON] Implementing Add Form Buttons in Django Inline Forms Set Part2

Introduction

The other day, I posted an article titled "Implementing an Add Form Button in a Django Inline Form Set" (https://qiita.com/3gou3/items/ffde3c787102972af6c3). Using the order entry form as a theme, when there were not enough detailed lines, you could increase the number of lines with the "Add line" button. The essential part was the implementation in jQuery, but even while writing the article, I felt that it was not very good writing. (Still, I'm having a hard time until I can write that, but ...) This time, I wrote it a little better, so I will share it.

environment

code

There are many things in common with the previous code, but I put it here again (https://github.com/sangou310/inlineformset_project_2.git). Only the necessary parts will be posted in the article.

What was dissatisfied

The problematic part is the part written in jQuery in the template file jutyu_form.html (the part of <script> ~ </ script> in the latter half).

--Similar settings of id and name attributes are repeated for the number of elements in the form, making it redundant. --This is because the element is specified directly by id. --It is troublesome to write when there are many elements. ――When the order changes or the elements of the form increase or decrease, it is necessary to change jQuery each time.

In Django, I think you use forms.py or a template file to specify the order of the elements in a form. In the previous code, the order of writing elements is related to the jQuery part as well. The sort order should be specified in forms.py etc., the jQuery part should be devoted to the process of adding rows, and the sort order should be as specified elsewhere. I don't want to do something similar here or there.

Improvement

The improvement is

--Select the element that needs to be changed programmatically without directly specifying it by id. --To make it easier, instead of letting Django render it as {{formset}} etc. when displaying the formset in the template file, write it yourself.

Method

For the management information required when adding rows in the form set, see [Last article](https://qiita.com/3gou3/items/ffde3c787102972af6c3#%E3%82%A4%E3%83%B3% E3% 83% A9% E3% 82% A4% E3% 83% B3% E3% 83% 95% E3% 82% A9% E3% 83% BC% E3% 83% A0% E3% 82% BB% E3% 83% 83% E3% 83% 88% E3% 81% AF% E3% 81% A9% E3% 81% 86% E3% 81% AE% E3% 82% 88% E3% 81% 86% E3% 81% AB% E7% AE% A1% E7% 90% 86% E3% 81% 95% E3% 82% 8C% E3% 81% A6% E3% 81% 84% E3% 82% 8B% E3% 81% AE% See E3% 81% 8B). The row to add is written in one row in the HTML table, that is, in <tr> ~ </ tr>, so

  1. Get the number of forms contained in the inline form set.
  2. Copy the first row (0th) of the form set and add it to the end of the table. Since the line copied in 1.1 may have already been entered, clear the entered contents.
  3. Rewrite id and name of the added line.
  4. Increase TOTAL_FORMS by 1.

It will be. The annotation numbers in the code correspond to the numbers above.

jutyu/jyutu_form.html (excerpt)


jQuery(function ($) {
    $("button#addForm").on("click", function () {
        //1. 1. Get the number of forms included in the inline form set (= TOTAL)_Get the FORMS value)
        const totalManageElement = $("input#id_jutyudetail_set-TOTAL_FORMS");
        const currentJutyuDetailCount = parseInt(totalManageElement.val());

        //2. Copy the first row (0th) of the form set and add it to the end of the table.
        $("table#detail-table tbody tr:first-child").clone().appendTo("table#detail-table tbody");
        //3. 3. Clear the input
        $("table#detail-table tbody tr:last-child").find("select, input").each(function () {
            //Clear (cut out) without distinguishing between select and input
            $(this).val("");
            $(this).prop("selected", false);  //It will be deselected without it. .. ..
            $(this).prop("checked", false);
            //4. Id of the added line,Rewrite name. ex. id_jutyudetail_set-0-part → id_jutyudetail_set-1-part
            var thisName = $(this).attr("name");
            thisName = thisName.replace("-0-", "-" + currentJutyuDetailCount + "-");
            $(this).attr("name", thisName);
            $(this).attr("id", "id_" + thisName);
        });
        // 5.TOTAL_Increase FORMS by 1
        $("input#id_jutyudetail_set-TOTAL_FORMS").val(currentJutyuDetailCount + 1);
    });
});

It's more about copying table rows with jQuery rather than Django. I'm sorry. For jQuery, you can do it this way! Please refrain from the explanation because it was only possible while checking.

The only thing you have to mess with the attributes in the copied line is the select and ʻinputelements (in this example). I was wondering how to select it, but when I gently select each line as the target$ ("table # detail-table tbody tr: last-child ")and select itfind about select and ʻinput It seemed easy, so I tried it. After that, for the selected one, clear the input contents and rewrite the name and id with .each.

Actually, it may be better to change the content of the clear depending on what kind of attribute the target element has, but it seems to be harmless, so I am doing the same.

To supplement the part related to Django, the line to be copied in step 2 is the first line. The first line-that is, the 0th (zero) th in the form set-will always exist, and the numbers included in the id and name attributes are still 0 (zero), so follow step 4 to enter the numbers. It is used as a mark when replacing.

It may be better to use a regular expression to specify the target character string when replacing, but since that part becomes a characteristic character string (" -0- "), what kind of regular expression will be used I chose solid writing rather than worrying about it.

In step 4, the value of the id attribute is the value of the name attribute with ʻid_` added to the beginning, so the id is set after creating the value of name first.

Finally

I have a lot of knowledge that I don't have enough now, so I think it's better to move it quickly in a way that you can understand rather than forcibly writing it in a cool way. I can't do it even if I want to write it in a cool way (T_T) I would appreciate it if you could tell me what is wrong or how to write it better.

Also, I hope it will be a hint for those who are suffering from the same thing. Isn't it difficult because you need a lot of knowledge before you get used to this part, which is likely to be necessary?

Recommended Posts

Implementing Add Form Buttons in Django Inline Forms Set Part2
Implement an add form button in your Django inline form set
Dynamically add form fields in Django
Forms in Django
Dynamically add fields to Form objects in Django
Set the form DateField to type = date in Django
Set placeholders in input fields in Django
[Django memo] I want to set the login user information in the form in advance