JavaScript构造含动态嵌套数组JSON及Django交互方案咨询
解决方案
前端JSON结构生成逻辑修复
你现有代码的核心问题是把单据对象初始化、JSON序列化的逻辑都放在了表格行遍历循环内部,导致每遍历一行就生成一个独立的单据对象,items数组自然永远只包含当前行的单个商品。
正确逻辑是先在循环外初始化单据主对象和空的items数组,遍历行时只把当前行的商品数据追加到items数组,所有行处理完成后再做统一序列化。另外注意把“是否是添加按钮行”的判断移到读取行元素之前,避免读到最后一行功能按钮时因找不到对应选择器报错。
修正后的核心代码如下:
// 优先指定tbody选择器,避免读取表头、表尾无关行 const tableTbody = document.querySelector(".invoice-add-table table tbody"); // 循环外初始化主单据对象 const invoice = { "doc_type": "capitalize", "date": document.querySelector('input[name="invoice-date"]').value, "number": 98, // 可替换为实际单据号取值逻辑 "contragent_id": document.querySelector('select').value, "comment": "", "items": [] }; for (let i = 0; i < tableTbody.rows.length; i++) { const row = tableTbody.rows[i]; // 碰到添加按钮行直接终止循环 if (row.classList.contains("add-elements")) break; // 读取当前行商品数据 const productId = row.cells[1].querySelector("span").dataset.id; const quantity = row.cells[2].querySelector("input").value; const buyPrice = row.cells[3].querySelector("input").value; const sellPrice = row.cells[5].querySelector("input").value; // 追加到items数组,注意做类型转换匹配目标结构的数字类型要求 invoice.items.push({ "product": Number(productId), "buy_price": buyPrice, "sell_price": sellPrice, "quantity": Number(quantity) }); } // 所有行处理完成后,按目标结构包一层数组再序列化 const requestData = [invoice]; const jsonStr = JSON.stringify(requestData); console.log(jsonStr); // 发AJAX时直接传jsonStr即可,记得添加请求头 Content-Type: application/json
更稳妥的遍历方式:你动态添加行时已经给业务行加了invoice-row类,可以直接通过类名选中所有商品行遍历,不需要依赖单元格索引,后续调整表格列顺序也不会读错数据:
document.querySelectorAll(".invoice-row").forEach(row => { const productId = row.querySelector("td.name span").dataset.id; const quantity = row.querySelector('input[name="quantity"]').value; const buyPrice = row.querySelector('input[name="last_buy_price"]').value; const sellPrice = row.querySelector('input[name="sell_price"]').value; invoice.items.push({/* 同上字段结构 */}); })
实现安全性说明
- 前端逻辑层面:只要做好基础输入校验(比如你已有的数字格式校验),前端拼接JSON本身没有安全风险,但所有前端传入的值都不可信,不要把权限校验、价格计算、金额汇总这类核心逻辑放在前端实现。
- 传输层面:站点开启HTTPS的前提下,AJAX传输过程不会被窃听篡改,提交时记得携带Django自带的CSRF令牌,避免跨站请求伪造攻击。
- 后端校验必须覆盖所有字段:不要直接拿前端传的商品价格、商品ID、合作方ID直接落库,必须校验对应数据是否存在、当前用户是否有权限操作、数值是否在合法区间,避免恶意用户篡改请求参数提交非法数据。
Django后端最优实现方案
完全不需要受限于formset的自定义能力,Django原生支持JSON请求体解析,灵活度远高于formset,实现步骤如下:
- 前端提交时在请求头中携带CSRF令牌,不需要关闭CSRF防护。
- 后端视图层直接解析JSON请求体处理即可,示例代码:
import json from django.http import JsonResponse from django.views.decorators.http import require_POST @require_POST def save_invoice(request): # 解析前端传入的JSON数据 req_data = json.loads(request.body) invoice_info = req_data[0] # 主表字段校验:自行补充日期合法性、合作方ID是否存在等校验逻辑 # 保存主表记录 invoice = Invoice.objects.create( doc_type=invoice_info.get("doc_type"), date=invoice_info.get("date"), number=invoice_info.get("number"), contragent_id=invoice_info.get("contragent_id"), comment=invoice_info.get("comment", "") ) # 循环处理商品明细 for item in invoice_info.get("items", []): # 明细字段校验:自行补充商品ID是否存在、价格/数量是否为合法正数等逻辑 InvoiceItem.objects.create( invoice=invoice, product_id=item.get("product"), buy_price=item.get("buy_price"), sell_price=item.get("sell_price"), quantity=item.get("quantity") ) return JsonResponse({"code": 0, "msg": "保存成功"})
如果需要更规范的参数校验逻辑,可以用序列化器做嵌套结构校验,对动态字段、嵌套数组的支持比formset好很多,完全可以满足自定义需求。
内容的提问来源于stack exchange,提问作者sk1p
相关产品推荐
相关产品推荐

