Django inlineformset标记克隆行删除及保存异常、校验行丢失问题求解
具体修改方案
1. 视图(views.py)修改
两处inlineformset_factory定义都将can_delete改为True,启用Django原生删除字段能力:
di_formset = inlineformset_factory( Device, DeviceInterface, fields=('moduletype', 'firstportid', 'lastportid'), widgets={ 'firstportid':TextInput(attrs={'placeholder': 'e.g. TenGigabitEthernet1/0/1'}), 'lastportid':TextInput(attrs={'placeholder':'eg. TenGigabitEthernet1/0/48'}) }, extra=1,max_num=3, can_delete=True # 此处修改 )
原有保存逻辑无需调整,formset会自动过滤标记为删除的行。
2. HTML模板修改
2.1 初始行渲染逻辑调整
新增form-row类标记每一行、添加原生DELETE隐藏字段、将删除按钮的id改为class避免重复:
{{di_form.management_form}} <div id = "rowAddition"> {% for form in di_form %} <div class="form-row"> <div class="row"> <div class="col-md-2"> <div class="form-group"> <label for="{{form.moduletype.id_for_label}}">Module Type<span class="text-danger">*</span></label> {{form.moduletype}} </div> </div> <div class="col-md-4"> <div class="form-group"> <label for="{{form.firstportid.id_for_label}}">First Port ID<span class="text-danger">*</span></label> {{form.firstportid}} </div> </div> <div class="col-md-4"> <div class="form-group"> <label for="{{form.lastportid.id_for_label}}">Last Port ID <span class="text-danger">*</span></label> {{form.lastportid}} </div> </div> <!-- 新增删除列 --> <div class="col-md-1"> <div class="form-group"> <div class="text-sm-center"> <br /> <button type="button" class="btn btn-outline-danger btn-rounded removerow" {% if forloop.first and di_form.total_form_count == 1 %}style="display: none;"{% endif %}> <i class="dripicons-minus"></i> </button> {{form.DELETE}} </div> </div> </div> </div> {{form.id}} </div> {%endfor%} <!-- 空表单移到外部避免被遍历 --> <div id="empty-form" class="form-row" style="display: none;"> <div class="row"> <div class="col-md-2"> <div class="form-group"> <label>Module Type<span class="text-danger">*</span></label> {{di_form.empty_form.moduletype}} </div> </div> <div class="col-md-4"> <div class="form-group"> <label>First Port ID<span class="text-danger">*</span></label> {{di_form.empty_form.firstportid}} </div> </div> <div class="col-md-4"> <div class="form-group"> <label>Last Port ID <span class="text-danger">*</span></label> {{di_form.empty_form.lastportid}} </div> </div> <div class="col-md-1"> <div class="form-group"> <div class="text-sm-center"> <br /> <button type="button" class="btn btn-outline-danger btn-rounded removerow"> <i class="dripicons-minus"></i> </button> {{di_form.empty_form.DELETE}} </div> </div> </div> </div> {{di_form.empty_form.id}} </div> <div id='morerows'></div> </div>
注意:所有表单输入框如果要触发修改确认逻辑,请添加onchange="portidChange(this.value)"属性
3. JS代码修改
修复表单前缀错误、新增索引重编逻辑、处理校验回显初始化:
const maxrow = parseInt($('#id_deviceinterface_set-MAX_NUM_FORMS').val()); const formPrefix = 'deviceinterface_set'; let changeFlag=0; // 公共函数:重新编号所有行索引,保证连续性 function renumberFormRows() { $('.form-row').each(function(index) { $(this).find('input, select, textarea, label').each(function() { const oldName = $(this).attr('name'); const oldId = $(this).attr('for') || $(this).attr('id'); if (oldName) { const newName = oldName.replace(new RegExp(`${formPrefix}-\\d+-`), `${formPrefix}-${index}-`); $(this).attr('name', newName); } if (oldId) { const newId = oldId.replace(new RegExp(`${formPrefix}-\\d+-`), `${formPrefix}-${index}-`); $(this).attr('for') ? $(this).attr('for', newId) : $(this).attr('id', newId); } }); }); // 更新总表单数和按钮状态 const total = $('.form-row').length; $('#id_' + formPrefix + '-TOTAL_FORMS').val(total); if (total >= maxrow) { $('#addrow').attr("disabled", true).attr("class","btn btn-rounded btn-danger").html("Reached max limit"); } else { $('#addrow').attr("disabled", false).attr("class","btn btn-outline-success btn-rounded").html("<i class='dripicons-plus'></i>"); } total == 1 ? $('.removerow').hide() : $('.removerow').show(); } // 页面加载时初始化,解决校验回显问题 $(document).ready(function() { renumberFormRows(); $('#deleteConfirmation').modal('hide'); }); function portidChange(val) { changeFlag = val ? 1 : 0; } // 新增行逻辑 $('#addrow').click(function () { const totalForms = parseInt($('#id_' + formPrefix + '-TOTAL_FORMS').val()); const newRow = $('#empty-form').html().replace(/__prefix__/g, totalForms); $('#morerows').append(newRow); renumberFormRows(); changeFlag=0; }); // 删除行逻辑 $(document).on('click', '.removerow', function () { const $row = $(this).closest('.form-row'); if(changeFlag==1) { $('#removerowConfirmed').off('click').on('click', function () { // 标记删除,不直接删DOM,Django会自动过滤 $row.find('input[name$="-DELETE"]').prop('checked', true); $row.hide(); $('#deleteConfirmation').modal('hide'); renumberFormRows(); }); $('#deleteConfirmation').modal('show'); } else { $row.find('input[name$="-DELETE"]').prop('checked', true); $row.hide(); renumberFormRows(); } });
内容的提问来源于stack exchange,提问作者Calvin Lin
相关产品推荐
相关产品推荐

