【问题标题】:Sequelize migration 'Can't create table (errno: 150 "Foreign key constraint is incorrectly formed")'Sequelize迁移'无法创建表(errno:150“外键约束形成错误”)'
【发布时间】:2021-12-24 01:30:48
【问题描述】:

在我的项目中添加 Sequelize 时,我在迁移和添加外键约束方面遇到了很大的困难。

文档...嗯,还有改进的余地!
根据我的在线搜索,似乎其他人也在苦苦挣扎。

想要以一对多关系关联两个表:

模型 1:用户状态(值:'init'、'active'、'inactive' ...)
模型 2:用户(用户名、电子邮件、pwdhash、...)

首先,创建迁移文件:

$ sequelize model:generate --name Userstatus --attributes name:string,value:tinyint,comment:string
$ sequelize model:generate --name User --attributes username:string,pwdhash:string,email:string,statusId:tinyint

结束这四个文件(包括修改):

  1. 迁移/xxxxxxxxxxxxx1-create-userstatus.js
"use strict";
module.exports = {
  up: async (queryInterface, Sequelize) => {
    await queryInterface.createTable("Userstatus", {
      id: {
        allowNull: false,
        autoIncrement: true,
        primaryKey: true,
        type: Sequelize.TINYINT,
      },
      name: {
        type: Sequelize.STRING,
      },
      comment: {
        type: Sequelize.STRING,
      },
    });
  },
  down: async (queryInterface, Sequelize) => {
    await queryInterface.dropTable("Userstatus");
  },
};
  1. 迁移/xxxxxxxxxxxxx2-create-users.js
"use strict";
module.exports = {
  up: async (queryInterface, Sequelize) => {
    await queryInterface.createTable("Users", {
      id: {
        allowNull: false,
        autoIncrement: true,
        primaryKey: true,
        type: Sequelize.BIGINT,
      },
      username: {
        type: Sequelize.STRING,
      },
      pwdhash: {
        type: Sequelize.STRING,
      },
      email: {
        type: Sequelize.STRING,
      },
      statusId: {
        type: Sequelize.TINYINT,
        references: {
          model: "Userstatus",
          key: "id",
        },
        onUpdate: "CASCADE",
        onDelete: "SET DEFAULT",
      },
      statusUntil: {
        allowNull: false,
        type: Sequelize.DATE,
      },
      createdAt: {
        allowNull: false,
        type: Sequelize.DATE,
      },
      updatedAt: {
        allowNull: false,
        type: Sequelize.DATE,
      },
      deletedAt: {
        allowNull: true,
        type: Sequelize.DATE,
      },
    });
  },
  down: async (queryInterface, Sequelize) => {
    await queryInterface.dropTable("Users");
  },
};
  1. models/userstatus.js(运行时,与迁移无关)
  2. models/user.js(运行时,与迁移无关)

执行命令:

$ sequelize db:migrate

Sequelize CLI [节点:14.17.0,CLI:6.2.0,ORM:6.9.0]

已加载配置文件“db\config\config.js”。
使用环境“开发”。
== xxxxxxxxxxxxx1-create-userstatus: 正在迁移 =======
== xxxxxxxxxxxxx1-create-userstatus: 已迁移 (0.101s)

== xxxxxxxxxxxxx2-create-users: 迁移 =======

错误:无法创建表myDb.Users(错误号:150“外键约束格式不正确”)

【问题讨论】:

    标签: mysql sequelize.js innodb sequelize-cli


    【解决方案1】:

    添加详细logging to config说明这是onDelete引起的MySQL错误(不接受SET DEFAULT):
    Can't create foreign key with ON DELETE SET DEFAULT

    在某个地方,我也遇到了由类型不匹配(TINYINT 与 INTEGER)引起的完全相同的错误响应:

    1. 迁移/xxxxxxxxxxxxx1-create-userstatus.js
    "use strict";
    module.exports = {
      up: async (queryInterface, Sequelize) => {
        await queryInterface.createTable("Userstatus", {
          id: {
            allowNull: false,
            autoIncrement: true,
            primaryKey: true,
            type: Sequelize.TINYINT,
          },
      :
    };
    
    1. 迁移/xxxxxxxxxxxxx2-create-users.js
    "use strict";
    module.exports = {
      up: async (queryInterface, Sequelize) => {
        await queryInterface.createTable("Users", {
          :
          statusId: {
            type: Sequelize.INTEGER,
            references: {
              model: "Userstatus",
              key: "id",
            },
            onUpdate: "CASCADE",
          },
      :
    };
    

    经验教训:发生迁移错误时,首先关注 DB 和 SQL - 确保生成的 SQL 在手动执行时确实有效。

    这是我个人最终花费的时间比需要更多的地方 - 在我知道发生了什么之前,我试图“修复”Sequelize 迁移......真正的修复是打开日志记录为了直接访问 SQL。

    更新:直接在创建表的位置定义外键约束,产生另一个需要处理的问题 - 为了撤消此类迁移,迁移的“撤消”部分需要保留额外的 queryInterface.removeConstraint(....) 命令,引入需要使用 Promise.all([...]) (example here)

    也许只是口味问题...我现在选择在单独的迁移文件中定义外键约束,如下所示:

    迁移/xxxxxxxxxxxxxxx-fk-userstatus-associate.js
    "use strict";
    module.exports = {
      up: async (queryInterface, Sequelize) => {
        return await queryInterface.addConstraint("Users", {
          type: "FOREIGN KEY",
          fields: ["statusId"], // field name of the foreign key
          name: "fk_users_statusId",
          references: {
            table: "Userstatus", // Target model
            field: "id", // key in Target model
          },
          onUpdate: "CASCADE",
          onDelete: "RESTRICT",
        });
      },
    
      down: async (queryInterface, Sequelize) => {
        return await queryInterface.removeConstraint(
          "Users", // Source model
          "fk_users_statusId" // key to remove
        );
      },
    };
    

    胜利:

    • 平滑撤消处理:$ sequelize db:migrate:undo
    • 我可以随意命名我的约束

    【讨论】:

      猜你喜欢
      • 2021-07-27
      • 2021-08-06
      • 2021-01-05
      • 2020-12-02
      • 2022-01-25
      • 2020-12-19
      • 2021-07-22
      • 1970-01-01
      • 2018-02-04
      相关资源
      最近更新 更多