0

0

如何使用单一主表ID批量保存多个子表表单

心靈之曲

心靈之曲

发布时间:2026-01-15 19:13:01

|

106人浏览过

|

来源于php中文网

原创

如何使用单一主表ID批量保存多个子表表单

本文介绍在django中通过一个通用id(如`mprep`实例)关联并批量保存多个`msform`子表单的完整实现方案,解决单次提交仅保存一条子记录的问题,并提供前端动态增行与后端正确处理的关键代码。

要实现“一次提交、多条子表单保存”,核心在于:使用 Django 的 modelformset_factory 替代单个 ModelForm,并确保主表(Mprep)实例在首次保存后获得有效主键(id),再将该 ID 正确赋值给每条子表单记录。原代码中 spe_form = MSForm(...) 仅创建单个表单,无法处理多行数据;且 prep.id = None 的写法不仅无效(prep 是主表对象,不应置空ID),还可能引发逻辑错误。

✅ 正确做法如下:

LibLib AI
LibLib AI

中国领先原创AI模型分享社区,拥有LibLib等于拥有了超多模型的模型库、免费的在线生图工具,不考虑配置的模型训练工具

下载

1. 后端:改用 modelformset_factory 处理多条子记录

from django.forms import modelformset_factory

def preps(request, prep=None):
    # 获取或初始化主表对象
    if prep:
        info = Mprep.objects.get(id=prep)
    else:
        info = Mprep()

    # 构建子表单集(支持0~n条记录)
    MSFormSet = modelformset_factory(
        model=MSprep,  # 子模型
        form=MSForm,
        extra=1,       # 默认显示1行空白
        can_delete=True
    )

    if request.method == 'POST':
        gen_form = MGForm(request.POST, instance=info)
        # 使用 queryset 绑定已有子记录(若存在),否则为空集
        spe_formset = MSFormSet(
            request.POST,
            queryset=MSprep.objects.filter(prep=info) if info.pk else MSprep.objects.none(),
            prefix='spe'
        )

        if gen_form.is_valid() and spe_formset.is_valid():
            # 先保存主表,确保 info 获得有效 id
            info = gen_form.save()  # 注意:必须赋值回 info,获取新生成的 pk

            # 批量保存子表单,并关联 prep 字段
            instances = spe_formset.save(commit=False)
            for instance in instances:
                instance.prep = info  # 关联主表
            # 批量入库
            for obj in instances:
                obj.save()
            # 处理被标记删除的记录
            for obj in spe_formset.deleted_objects:
                obj.delete()

            messages.success(request, '所有信息保存成功')
            return redirect('preps_view', prep=info.id)
        else:
            messages.error(request, '表单填写有误,请检查')
    else:
        gen_form = MGForm(instance=info)
        spe_formset = MSFormSet(
            queryset=MSprep.objects.filter(prep=info) if info.pk else MSprep.objects.none(),
            prefix='spe'
        )

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

2. 前端模板:渲染 Formset 并支持动态增行(兼容 Django 表单集)

<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>

  <!-- 子表单集 -->
  <table class="table mt-4">
    <thead>
      <tr>
        <th>名称</th>
        <th>数值</th>
        <th>单位</th>
        <th>操作</th>
      </tr>
    </thead>
    <tbody>
      {% for form in spe_formset %}
        <tr>
          <td>{{ form.name }}</td>
          <td>{{ form.value }}</td>
          <td>{{ form.unit }}</td>
          <td>{{ form.DELETE }} 删除</td>
          {{ form.id }}
        </tr>
      {% endfor %}
      {{ spe_formset.management_form }} {# 必须包含 #}
    </tbody>
  </table>

  <input type="submit" class="btn btn-primary mt-3" value="保存全部">
</form>

<!-- 动态添加新行(需配合 JS 初始化空行) -->
<script>
  // 简化版:点击新增一行(基于 Django formset 的命名规则)
  document.getElementById("add-row-button").addEventListener("click", function () {
    const tbody = document.querySelector("table tbody");
    const totalForms = parseInt(document.querySelector("[name='spe-TOTAL_FORMS']").value);

    const newRow = document.createElement("tr");
    newRow.innerHTML = `
      <td><input type="text" name="spe-${totalForms}-name" class="form-control"></td>
      <td><input type="number" name="spe-${totalForms}-value" class="form-control"></td>
      <td><input type="text" name="spe-${totalForms}-unit" class="form-control"></td>
      <td><input type="checkbox" name="spe-${totalForms}-DELETE"> 删除</td>
      <input type="hidden" name="spe-${totalForms}-id" value="">
    `;

    tbody.appendChild(newRow);

    // 更新 TOTAL_FORMS
    document.querySelector("[name='spe-TOTAL_FORMS']").value = totalForms + 1;
  });
</script>

⚠️ 关键注意事项

  • spe_formset.management_form 必须渲染(通常隐藏),否则 Django 拒绝 POST;
  • 主表 info = gen_form.save() 必须赋值,确保后续子表单能读取其 pk;
  • 子表单 queryset 应基于 info.pk 动态过滤,避免未保存主表时查询异常;
  • 若需严格校验(如不允许空行),可在 MSForm.clean() 中添加逻辑;
  • 生产环境建议为动态行添加客户端验证(如禁用空提交)+ 后端二次校验。

通过以上重构,即可安全、稳定地实现「一个主ID,N条子记录」的一键批量保存,兼顾可扩展性与数据一致性。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
Python Web 框架 Django 深度开发
Python Web 框架 Django 深度开发

本专题系统讲解 Python Django 框架的核心功能与进阶开发技巧,包括 Django 项目结构、数据库模型与迁移、视图与模板渲染、表单与认证管理、RESTful API 开发、Django 中间件与缓存优化、部署与性能调优。通过实战案例,帮助学习者掌握 使用 Django 快速构建功能全面的 Web 应用与全栈开发能力。

153

2026.02.04

C++高性能网络编程与Reactor模型实践
C++高性能网络编程与Reactor模型实践

本专题围绕 C++ 在高性能网络服务开发中的应用展开,深入讲解 Socket 编程、多路复用机制、Reactor 模型设计原理以及线程池协作策略。内容涵盖 epoll 实现机制、内存管理优化、连接管理策略与高并发场景下的性能调优方法。通过构建高并发网络服务器实战案例,帮助开发者掌握 C++ 在底层系统与网络通信领域的核心技术。

0

2026.03.03

Golang 测试体系与代码质量保障:工程级可靠性建设
Golang 测试体系与代码质量保障:工程级可靠性建设

Go语言测试体系与代码质量保障聚焦于构建工程级可靠性系统。本专题深入解析Go的测试工具链(如go test)、单元测试、集成测试及端到端测试实践,结合代码覆盖率分析、静态代码扫描(如go vet)和动态分析工具,建立全链路质量监控机制。通过自动化测试框架、持续集成(CI)流水线配置及代码审查规范,实现测试用例管理、缺陷追踪与质量门禁控制,确保代码健壮性与可维护性,为高可靠性工程系统提供质量保障。

65

2026.02.28

Golang 工程化架构设计:可维护与可演进系统构建
Golang 工程化架构设计:可维护与可演进系统构建

Go语言工程化架构设计专注于构建高可维护性、可演进的企业级系统。本专题深入探讨Go项目的目录结构设计、模块划分、依赖管理等核心架构原则,涵盖微服务架构、领域驱动设计(DDD)在Go中的实践应用。通过实战案例解析接口抽象、错误处理、配置管理、日志监控等关键工程化技术,帮助开发者掌握构建稳定、可扩展Go应用的最佳实践方法。

57

2026.02.28

Golang 性能分析与运行时机制:构建高性能程序
Golang 性能分析与运行时机制:构建高性能程序

Go语言以其高效的并发模型和优异的性能表现广泛应用于高并发、高性能场景。其运行时机制包括 Goroutine 调度、内存管理、垃圾回收等方面,深入理解这些机制有助于编写更高效稳定的程序。本专题将系统讲解 Golang 的性能分析工具使用、常见性能瓶颈定位及优化策略,并结合实际案例剖析 Go 程序的运行时行为,帮助开发者掌握构建高性能应用的关键技能。

44

2026.02.28

Golang 并发编程模型与工程实践:从语言特性到系统性能
Golang 并发编程模型与工程实践:从语言特性到系统性能

本专题系统讲解 Golang 并发编程模型,从语言级特性出发,深入理解 goroutine、channel 与调度机制。结合工程实践,分析并发设计模式、性能瓶颈与资源控制策略,帮助将并发能力有效转化为稳定、可扩展的系统性能优势。

23

2026.02.27

Golang 高级特性与最佳实践:提升代码艺术
Golang 高级特性与最佳实践:提升代码艺术

本专题深入剖析 Golang 的高级特性与工程级最佳实践,涵盖并发模型、内存管理、接口设计与错误处理策略。通过真实场景与代码对比,引导从“可运行”走向“高质量”,帮助构建高性能、可扩展、易维护的优雅 Go 代码体系。

20

2026.02.27

Golang 测试与调试专题:确保代码可靠性
Golang 测试与调试专题:确保代码可靠性

本专题聚焦 Golang 的测试与调试体系,系统讲解单元测试、表驱动测试、基准测试与覆盖率分析方法,并深入剖析调试工具与常见问题定位思路。通过实践示例,引导建立可验证、可回归的工程习惯,从而持续提升代码可靠性与可维护性。

4

2026.02.27

漫蛙app官网链接入口
漫蛙app官网链接入口

漫蛙App官网提供多条稳定入口,包括 https://manwa.me、https

336

2026.02.27

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Bootstrap 5教程
Bootstrap 5教程

共46课时 | 3.5万人学习

AngularJS教程
AngularJS教程

共24课时 | 4万人学习

CSS教程
CSS教程

共754课时 | 38.8万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号