Sequelize - 与自定义外键的 1:M 关系不会在级联上删除

问题描述 投票:0回答:1

我有 2 个型号:

User
Profile

最初,我建立了

User
Profile
的关联:

User.Profile = User.hasOne(models.Profile, {
    as: "profile",
    foreignKey: "userId",
    onDelete: "CASCADE",
    onUpdate: "CASCADE",
});

我设置了一个测试来检查模型之间的关联

const user = await User.create(
    {
        email: "[email protected]",
        password: bcrypt.hashSync("password", 10),
        role: "basic",
        profile: ...,
    },
    { include: ["profile"] }
);

// Check amount of entries created
expect(await User.count()).toBe(1);
expect(await Profile.count()).toBe(1);

// Check associations
expect(user.profile).toBeTruthy();


// Delete the user
await user.destroy();

// Check amount of entries deleted
expect(await User.count()).toBe(0);
expect(await Profile.count()).toBe(0);

到目前为止一切正常。现在我想确保协会能够双向运作。因此,我将以下关联添加到 Profile 模型中:

Profile.User = Profile.belongsTo(models.User, {
    foreignKey: "userId",
    as: "user",
});

我必须手动添加外键为

userId
,因为否则它会搜索字段
UserId
,使用大写“U”,因为表“User”采用PascalCase

但是现在,我已经通过的测试突然失败了:

{
    name: 'SequelizeForeignKeyConstraintError',
    parent: [Error: SQLITE_CONSTRAINT: FOREIGN KEY constraint failed] {
        errno: 19,
        code: 'SQLITE_CONSTRAINT',
        sql: 'DELETE FROM `Users` WHERE `id` = 1'
    },
    original: [Error: SQLITE_CONSTRAINT: FOREIGN KEY constraint failed] {
        errno: 19,
        code: 'SQLITE_CONSTRAINT',
        sql: 'DELETE FROM `Users` WHERE `id` = 1'
    },
    sql: 'DELETE FROM `Users` WHERE `id` = 1',
    parameters: {},
    table: undefined,
    fields: undefined,
    value: undefined,
    index: undefined,
    reltype: undefined
}

我在这里做错了什么?

其他文件

作为参考,“用户”和“个人资料”的迁移如下所示:

"use strict";
/** @type {import('sequelize-cli').Migration} */
module.exports = {
    async up(queryInterface, Sequelize) {
        await queryInterface.createTable("Users", {
            id: { allowNull: false, autoIncrement: true, primaryKey: true, type: Sequelize.INTEGER },
            // ... other fields
        });
    },
    async down(queryInterface, Sequelize) {
        await queryInterface.dropTable("Users");
    },
};

"use strict";
/** @type {import('sequelize-cli').Migration} */
module.exports = {
    async up(queryInterface, Sequelize) {
        await queryInterface.createTable("Profiles", {
            id: { allowNull: false, autoIncrement: true, primaryKey: true, type: Sequelize.INTEGER },
            userId: {
                type: Sequelize.INTEGER,
                allowNull: false,
                unique: true,
                references: { model: "Users", key: "id" },
                onUpdate: "CASCADE",
                onDelete: "CASCADE",
            },
            // ... other fields
        });
    },
    async down(queryInterface, Sequelize) {
        await queryInterface.dropTable("Profiles");
    },
};

node.js sequelize.js
1个回答
0
投票

我知道这不是

CASCADE DELETE
的解决方案,但这是我的观点,可以解决当前的情况。

根据 StackOverflow 中的许多争论,我了解到,当您定义外键时,Sequelize 可能不会按预期运行。 它也可能取决于 MySQL 数据库。

作为程序员(不是数据库工程师),我更喜欢明确地控制进程,而不是“让数据库处理其他东西”。

所以我建议在 User 模型中添加 before destroy 钩子以控制删除逻辑,而不是将其信任给 db:

User.beforeDestroy(async user => {   
  await Profile.destroy({ where: { userId: user.id } });
  // also can add extra operations like logging and etc here...
});

实例挂钩

中阅读相关内容

另一种解决方案不是真正删除记录,而是使用 Sequelize 的 PARANOID MODE

隐藏它们

Sequelize 支持偏执表的概念。偏执表是这样一种表,当被告知删除一条记录时,它不会真正删除它。相反,名为deletedAt的特殊列将其值设置为该删除请求的时间戳。

这意味着偏执表执行记录的软删除,而不是硬删除。

© www.soinside.com 2019 - 2024. All rights reserved.