You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于唯一ID批量保存spe_form表单的技术实现求助

批量保存多个spe_form表单的解决方案

问题背景

现有代码仅支持保存单个spe_form表单,需要实现点击保存按钮时,通过gen_form的唯一ID批量保存多个spe_form表单。


后端代码修改(使用Django FormSet)

借助Django的formset_factory处理多表单批量提交,替换原单个表单逻辑:

from django.forms import formset_factory

def preps(request, prep=None):
    # 修正原参数判断逻辑
    if prep:
        info = Mprep.objects.get(id=prep)
    else:
        info = Mprep()
    
    # 创建表单集,默认显示1个空表单
    MSFormSet = formset_factory(MSForm, extra=1)
    
    if request.method == 'POST':
        gen_form = MGForm(request.POST, instance=info)
        # 实例化表单集,传入POST数据与前缀
        form_set = MSFormSet(request.POST, prefix='preps')

        if gen_form.is_valid() and form_set.is_valid():
            # 保存主表单并获取实例
            saved_gen = gen_form.save()
            # 遍历表单集,批量保存子表单
            for form in form_set:
                if form.cleaned_data:  # 跳过空表单
                    spe_instance = form.save(commit=False)
                    spe_instance.prep = saved_gen
                    spe_instance.save()
            messages.success(request, '信息保存成功')
            return redirect('preps_view', prep=saved_gen.id)
        else:
            messages.error(request, '保存信息出错')
    else:
        gen_form = MGForm(instance=info)
        form_set = MSFormSet(prefix='preps')

    context = {'gen_form': gen_form, 'spe_form_set': form_set}
    return render(request, 'base.html', context)

前端模板调整

修改HTML模板,正确渲染表单集并保留添加行功能:

<div class="container mt-5">
  <h1>Measurements Prep</h1>
  <form method="post">
    {% csrf_token %}
    <div class="form-row">
      <div class="form-group col-4">
        {{ gen_form.project|as_crispy_field }}
      </div>
      <div class="form-group col-4">
        {{ gen_form.cell|as_crispy_field }}
      </div>
      <div class="form-group col-4">
        {{ gen_form.sample|as_crispy_field }}
      </div>
    </div>
    <br>
    <!-- 必须渲染表单集管理组件,用于Django识别表单数量 -->
    {{ spe_form_set.management_form }}
    <table id="myTable" class="table" data-toggle="table" data-mobile-responsive="true" data-pagination="true"
      data-height="460">
      <thead>
        <tr>
          <th>名称</th>
          <th>数值</th>
          <th>单位</th>
        </tr>
      </thead>
      <tbody>
        <!-- 遍历渲染表单集中的所有表单 -->
        {% for form in spe_form_set %}
        <tr class="form-row">
          <td>{{ form.name|as_crispy_field }}</td>
          <td>{{ form.value|as_crispy_field }}</td>
          <td>{{ form.unit|as_crispy_field }}</td>
        </tr>
        {% endfor %}
      </tbody>
    </table>
    <button id="add-row-button" class="btn btn-primary mt-4">添加行</button>
    <input type="submit" class="btn btn-primary mt-4" value="保存">
  </form>
</div>

JavaScript动态添加行逻辑调整

动态生成符合Django表单集格式的新行,更新表单计数:

document.getElementById("add-row-button").addEventListener("click", function () {
  // 获取表单集总数量控件
  var totalForms = document.getElementById('id_preps-TOTAL_FORMS');
  var formCount = parseInt(totalForms.value);
  
  var tableBody = document.querySelector("#myTable tbody");
  var newRow = document.createElement("tr");
  newRow.className = "form-row";
  
  // 替换空表单中的占位符__prefix__为当前表单索引
  newRow.innerHTML = `
    <td>${"{{ spe_form_set.empty_form.name|as_crispy_field }}".replace(/__prefix__/g, formCount)}</td>
    <td>${"{{ spe_form_set.empty_form.value|as_crispy_field }}".replace(/__prefix__/g, formCount)}</td>
    <td>${"{{ spe_form_set.empty_form.unit|as_crispy_field }}".replace(/__prefix__/g, formCount)}</td>
  `;
  
  tableBody.appendChild(newRow);
  // 更新表单集总数量
  totalForms.value = formCount + 1;
});

关键注意事项

  • 必须渲染{{ spe_form_set.management_form }},Django依赖它跟踪表单集中的表单数量与索引
  • 动态添加表单时,需将empty_form中的__prefix__替换为当前表单计数,确保字段name格式为preps-<index>-name,符合Django表单集识别规则
  • 保存时过滤空表单,避免创建无数据的数据库实例

内容的提问来源于stack exchange,提问作者Munther Odeh

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 21:30:54