Django使用form repeater实现同字段多实例录入的最佳方法
自定义Recipe类多食材关联录入(表单重复器)最优实现方案
模型层设计
首先拆分数据结构,不要把多组食材字段直接堆在Recipe主模型里,用一对多关联存储食材条目:
- 主表
Recipe存储食谱本身的核心属性,比如食谱名称、做法描述、创建时间等 - 关联表
IngredientItem存储单组食材数据,和Recipe建立外键关联,单条数据对应单组食材结构
示例模型代码:
from django.db import models class Recipe(models.Model): name = models.CharField(max_length=200, verbose_name="食谱名称") description = models.TextField(verbose_name="食谱描述", blank=True) create_time = models.DateTimeField(auto_now_add=True) class IngredientItem(models.Model): recipe = models.ForeignKey(Recipe, on_delete=models.CASCADE, related_name="ingredients") ingredient = models.CharField(max_length=100, verbose_name="食材名称") measurement_unit = models.CharField(max_length=20, verbose_name="计量单位") unit_value = models.FloatField(verbose_name="计量数值")
单组食材数据结构参考:
ingredient = Lemon measurement_unit = ml unit_value = 100
前端表单重复器交互实现
不需要引入重型第三方UI组件,用少量原生JS/jQuery就能实现点击新增行、删除行的交互,完全匹配需要的效果:
- 页面初始加载时默认渲染1组食材录入行,每行包含3个输入框分别对应食材名、计量单位、计量数值,每行末尾放置「删除」按钮,表单区域顶部放置「+ 新增食材行」按钮
- 给新增按钮绑定点击事件:每次点击克隆现有行的DOM结构,清空输入框内的已有值,追加到食材录入容器末尾;所有输入框的
name属性统一使用数组格式命名,比如ingredient[]、measurement_unit[]、unit_value[],方便后端批量接收数据 - 给删除按钮绑定事件:点击后移除当前行DOM,逻辑上限制至少保留1行录入项,避免提交空数据
注意:不要给动态生成的输入框生成随机name属性,统一用数组格式命名可以大幅降低后端数据处理的复杂度
后端提交保存逻辑
- 首先校验Recipe主表字段,校验通过后先保存Recipe实例,获取生成的主键ID
- 从提交的表单数据中批量取出三个数组参数:食材名列表、计量单位列表、计量数值列表,先做基础校验:三个数组长度必须一致,计量数值必须为合法数字,完成非空校验
- 按索引对齐三组数据,批量生成
IngredientItem实例,用批量写入方法存入数据库,关联到刚保存的Recipe实例,减少数据库查询次数 - 如果校验不通过,返回错误信息的同时保留用户已经填写的所有行数据,不要刷新页面清空已填内容
如果是基于Django框架开发,不需要额外引入django-formset之类的第三方表单库,上述实现总代码量不超过100行,定制自由度远高于封装好的第三方组件,也不会出现依赖版本兼容问题,完全满足点击新增录入行、所有数据填写完成后统一保存Recipe的需求。
内容的提问来源于stack exchange,提问作者Oz Kan
相关产品推荐
相关产品推荐

