Sequelize模型paranoid为false时,如何在调用destroy时指定软删除
现有一个Sequelize模型Country,未开启paranoid(默认false),但已定义createdAt、updatedAt、deletedAt字段:
const Country = sequelize.define('Country', { name: { type: DataTypes.STRING, defaultValue: '', }, code: { type: DataTypes.STRING, defaultValue: '', }, currency: { type: DataTypes.STRING, defaultValue: '', }, languages: { type: DataTypes.STRING, defaultValue: '', }, id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true }, createdAt: DataTypes.DATE, updatedAt: DataTypes.DATE, deletedAt: DataTypes.DATE });
当前调用Model.destroy()会执行硬删除,需求是:保持模型paranoid: false不变,默认仍执行硬删除,仅在显式指定参数时才执行软删除。
项目背景:现有100+模型均未设置paranoid,但都包含上述三个时间字段,且所有Model.destroy()调用都被统一封装在一个函数中,希望在不修改模型定义的前提下,通过改造这个封装函数实现需求,避免直接编写原生SQL更新deletedAt的生硬方案。
方法1:用Model.update()实现软删除逻辑
在统一封装的删除函数中新增可选参数(如softDelete: boolean),当参数为true时,不调用destroy(),而是通过update()将deletedAt设置为当前时间,同时自动维护updatedAt字段(Sequelize默认会更新该字段,若模型开启时间戳)。
示例代码:
// 统一封装的删除函数 async function deleteRecord(model, whereCondition, options = {}) { if (options.softDelete) { // 执行软删除:更新deletedAt为当前时间 return await model.update( { deletedAt: new Date() }, { where: whereCondition, individualHooks: true } // 触发模型的update钩子 ); } else { // 默认执行硬删除 return await model.destroy({ where: whereCondition, individualHooks: true }); } } // 使用示例 // 硬删除(默认) await deleteRecord(Country, { id: 1 }); // 软删除(显式指定) await deleteRecord(Country, { id: 1 }, { softDelete: true });
该方案无需修改任何模型定义,基于Sequelize原生方法实现,能触发模型钩子函数,比原生SQL更优雅。
方法2:临时动态切换模型的paranoid属性
在封装函数中,当需要软删除时,临时将模型的paranoid设为true,执行destroy()后再恢复原设置,直接复用Sequelize原生的软删除逻辑。
示例代码:
async function deleteRecord(model, whereCondition, options = {}) { if (options.softDelete) { // 保存原paranoid配置 const originalParanoid = model.options.paranoid; // 临时开启软删除模式 model.options.paranoid = true; try { // 此时destroy会自动执行软删除逻辑 const result = await model.destroy({ where: whereCondition, individualHooks: true }); return result; } finally { // 恢复原配置,避免影响后续操作 model.options.paranoid = originalParanoid; } } else { return await model.destroy({ where: whereCondition, individualHooks: true }); } }
注意:Node.js单线程环境下不会有并发竞态问题,多线程场景需额外处理锁机制。该方案会触发Sequelize软删除相关的钩子函数,与原生开启paranoid的行为完全一致。
方法3:自定义模型软删除方法(备选)
若项目存在基础模型类,可通过继承给所有模型添加softDestroy()方法,但该方案需要修改模型继承关系,优先级低于前两种:
// 基础模型类 class BaseModel extends Model { static async softDestroy(options = {}) { return await this.update( { deletedAt: new Date() }, { ...options, individualHooks: true } ); } } // 所有模型继承BaseModel const Country = sequelize.define('Country', { /* 字段定义 */ }, { modelName: 'Country' }); Country.init({}, { sequelize, modelName: 'Country', extends: BaseModel });
后续在封装函数中根据参数判断调用softDestroy()或destroy()即可。
内容的提问来源于stack exchange,提问作者Hussain Ali Akbar

