如何用JavaScript删除Django Formset额外表单?附现有代码
Got it, let's tackle this formset deletion issue step by step. First, I notice you've already got the add functionality working, so we'll build on that to add both automatic deletion based on the visitor count dropdown and manual delete buttons for individual forms.
1. First, Configure Your Formset in the Backend
To make sure Django properly handles form deletion (even if we're removing form blocks via JS), update your formset definition to include can_delete=True (this adds a hidden DELETE field to each form, which helps Django track deletions if needed):
from django.forms import formset_factory # Replace VisitorForm with your actual form class VisitorFormSet = formset_factory(VisitorForm, extra=1, can_delete=True)
2. Update Your Template Structure
We need to wrap each form in the formset with a container class (to target them easily) and add a delete button to each form block:
<div id="userform"> {% for form_data in formset %} <!-- Add a container class for each form block --> <div class="visitor-form mb-3"> {{ form_data.management_form }} {{form_data.first_name|as_crispy_field}} {{form_data.last_name|as_crispy_field}} {{form_data.middle_name|as_crispy_field}} {{form_data.date_of_birth|as_crispy_field}} {{form_data.gender|as_crispy_field}} {{form_data.passport|as_crispy_field}} {{form_data.passport_ex|as_crispy_field}} {{form_data.nationality|as_crispy_field}} <!-- Add delete button --> <button type="button" class="btn btn-danger delete-form">Delete This Visitor</button> </div> {% endfor %} </div>
3. Update Your JavaScript Code
We'll modify your existing JS to handle deletion, fix form indexing after deletion, and sync the visitor count dropdown.
Full Updated JavaScript:
$(document).ready(function(){ $('.menu').hide("slide"); $('#show').click(function() { $('.menu').toggle("slide"); }); $('#hide').click(function() { $('.menu').hide("slide"); }); // Handle visitor count dropdown change $('#id_number_of_vistor').change(()=>{ var selectedCount = parseInt($('#id_number_of_vistor option:selected').text()); var totalForms = parseInt($('#id_Visitor-TOTAL_FORMS').val()); if (selectedCount > totalForms) { // Add missing forms for (let i = totalForms; i < selectedCount; i++) { cloneMore('#userform .visitor-form:last','Visitor'); } } else if (selectedCount < totalForms) { // Remove extra forms from the end let formsToRemove = totalForms - selectedCount; $('.visitor-form').slice(-formsToRemove).remove(); // Reindex remaining forms and update total reindexForms('Visitor'); } }); // Handle manual delete button clicks $(document).on('click', '.delete-form', function() { var formBlock = $(this).closest('.visitor-form'); // Ensure we keep at least one form if ($('.visitor-form').length > 1) { formBlock.remove(); reindexForms('Visitor'); // Sync the dropdown with the remaining form count $('#id_number_of_vistor').val($('.visitor-form').length); } else { alert("You need at least one visitor form!"); } }); }); // Updated cloneMore function to target the last form and add the container class function cloneMore(selector, type) { var newElement = $(selector).clone(true); // Reset all input values newElement.find(':input').each(function() { var total = $('#id_' + type + '-TOTAL_FORMS').val(); var name = $(this).attr('name').replace(/-\d+-/, '-' + total + '-'); var id = 'id_' + name; $(this).attr({'name': name, 'id': id}).val('').removeAttr('checked'); }); // Update label for attributes newElement.find('label').each(function() { var total = $('#id_' + type + '-TOTAL_FORMS').val(); var newFor = $(this).attr('for').replace(/-\d+-/, '-' + total + '-'); $(this).attr('for', newFor); }); // Increment total forms count var total = parseInt($('#id_' + type + '-TOTAL_FORMS').val()) + 1; $('#id_' + type + '-TOTAL_FORMS').val(total); // Append new form after the last one $(selector).after(newElement); } // Reindex forms after deletion to fix name/id attributes function reindexForms(type) { var forms = $('.visitor-form'); $('#id_' + type + '-TOTAL_FORMS').val(forms.length); forms.each(function(index) { // Update all input names and IDs $(this).find(':input').each(function() { var name = $(this).attr('name').replace(/-\d+-/, '-' + index + '-'); var id = 'id_' + name; $(this).attr({'name': name, 'id': id}); }); // Update all label for attributes $(this).find('label').each(function() { var newFor = $(this).attr('for').replace(/-\d+-/, '-' + index + '-'); $(this).attr('for', newFor); }); }); }
Key Changes Explained:
visitor-formClass: Wraps each form block so we can easily target and manipulate individual forms.reindexFormsFunction: Fixes thenameandidattributes of remaining forms after deletion, which is critical for Django to process the formset correctly (no broken index gaps).- Dropdown Sync: When you delete a form manually, the visitor count dropdown updates to match the current number of forms, and vice versa.
- Minimum Form Check: Ensures users can't delete the last remaining form (adjust this if your use case allows zero forms).
This should fully implement both automatic and manual deletion functionality for your formset.
内容的提问来源于stack exchange,提问作者Mhadi Ahmed

