PG Promise结合ColumnSet实现表单字段循环插入时校验器未触发求助
PG Promise 批量插入时校验器不触发的问题排查与修复
核心原因
你碰到的“PG Promise未调用校验器”的问题,本质是配置缺失加逻辑bug叠加导致的,和PG Promise本身的运行机制无关:
- 当前初始化的ColumnSet完全没有挂载任何自定义校验函数。传入ColumnSet的是纯字符串组成的列名数组,这种配置方式仅支持基础类型的SQL转义,PG Promise根本不会识别你在外部写的自定义逻辑,自然不会主动触发校验。
- 代码里的数值处理逻辑和事务执行时序完全错位,部分代码块因为逻辑错误根本没执行,导致数据没有进入批量插入流程,校验环节自然不会被触发。
具体问题拆解
- ColumnSet配置缺少校验器挂载位置
PG Promise的列校验逻辑必须直接写在ColumnSet的列对象配置中,只有在生成插入SQL时才会逐列执行。你之前用纯字符串定义列的写法,没有预留任何校验逻辑的注入点,相当于从根上没给校验器留执行入口。 - 变量初始化时序错误
你在路由函数最外层定义初始values数组时,直接引用了两个还在数据库事务中异步查询的变量:companyDetails和insertedForm。这两个变量在事务执行前是未定义状态,导致初始写入的两条数据本身就是无效值,根本走不到校验环节。 - 循环逻辑完全失效
你写的目标字段遍历循环for (var x = 1; x >= 7; x++)判断条件写反了,初始值x=1时,判断条件x>=7直接不成立,整个循环一次都不会执行,所有表单扩展字段根本没有被收集到values数组中。 - 自定义工具函数和PG Promise机制脱节
你写的arrayCheck只是普通的数组处理工具函数,不属于PG Promise的ColumnSet校验体系,PG Promise在生成SQL时不会主动调用这个函数,更不会把它当成校验器执行。
正确实现方式
- 先初始化带校验逻辑的ColumnSet,把校验规则直接绑定到每一列的配置上,生成SQL时会自动执行:
const cs = new pgp.helpers.ColumnSet([ { name: 'attrib_id', // 校验逻辑直接写在列配置的init钩子中,不符合规则直接抛错中断流程 init: col => { const id = Number(col.value); if (!Number.isInteger(id) || id <= 0) { throw new Error('attrib_id 必须为正整数'); } return id; } }, { name: 'value', init: col => { const val = col.value?.toString().trim(); if (!val) { throw new Error('表单字段值不能为空'); } return val; } }, { name: 'response_id', init: col => Number(col.value) } ], { table: 'formquestionresponse' }); - 调整事务内的代码执行顺序,等数据库查询拿到所有依赖变量后,再初始化values数组,修正循环条件:
db.tx(async t => { // 先查询关联的企业信息 const companyDetails = await db.one( "SELECT company_name, c.id, first_name, last_name FROM company c INNER JOIN useraccount u on c.id = u.company_id WHERE u.username = $1", [req.user.user] ); // 插入主表记录拿到response_id const insertedForm = await db.one( "INSERT INTO formresponse(company_id, form_id) VALUES ($1, 3) RETURNING response_id", [companyDetails.id] ); // 所有依赖变量拿到后再初始化values数组,避免引用未定义值 const values = [ { attrib_id: 20, value: companyDetails.company_name, response_id: insertedForm.response_id }, { attrib_id: 19, value: `${companyDetails.first_name} ${companyDetails.last_name}`, response_id: insertedForm.response_id } ]; // 修正循环判断条件,遍历1到7的表单字段 for (let x = 1; x <= 7; x++) { if (!isEmpty(req.body[`goal${x}`])) { values.push({ attrib_id: 23, value: req.body[`goal${x}`], response_id: insertedForm.response_id }); } if (!isEmpty(req.body[`descriptionofneed${x}`])) { values.push({ attrib_id: 24, value: req.body[`descriptionofneed${x}`], response_id: insertedForm.response_id }); } } // 生成插入SQL时,会自动逐行逐列执行ColumnSet上绑定的校验器 const insertQuery = pgp.helpers.insert(values, cs); await t.none(insertQuery); })
内容的提问来源于stack exchange,提问作者sylargaf
相关产品推荐
相关产品推荐

