【问题标题】:SequelizeJS migration: Adding multiple foreign keys to the same model?SequelizeJS 迁移:向同一个模型添加多个外键?
【发布时间】:2017-06-07 08:49:24
【问题描述】:

使用sequelize cli 实用程序的迁移功能时,可以通过添加新列来创建新的外键。但是,我在尝试为同一模型创建多个外键时遇到了错误。运行sequelize db:migrate 生成的外键名称没有区别。数据库引擎要求为所有外键分配一个唯一的名称,但 SequelizeJS 似乎最初将所有外键命名为:

// Excerpt from sequelize/lib/dialects/mysql/query-generator.js
// Line 195
{fkName: this.quoteIdentifier(attrName + '_foreign_idx')}

如果索引号 idx 没有正确递增,这当然会产生相等的键。

在 Sequelize 库中的某处,使用 db.sync() 初始化模型时,_foreign_idxidx 部分必须替换为实际数值,但我无法确定在哪里。我还通过检查另一个数据库中的外键验证了使用db.sync() 时,sequelize 确实增加了索引值。在该数据库中,外键被命名为_ibfk_1_ibfk_2、..、_ibfk_n

  • 有没有人遇到过外键生成器的问题 产生相同的名称?
  • 有没有人建议如何 使用 Sequelize 迁移时可以避免/缓解这种情况吗?

我使用 MySQL 作为数据库引擎,但外键名称生成器遵循相同的过程,例如。 postgre 也是如此,就我能够解释 sequelize 源代码而言。

示例

以下迁移将创建三个模型,并使用来自sequelize cli 的函数 addColumn 函数在它们之间创建关系。

脚本模拟了一个场景,其中一个教练和一个团队都由一些赞助商赞助。在这个模型中,我们希望在我们的教练和团队模型中都引用赞助商 ID。不幸的是,这将创建两个名为 sponsorId_foreign_idx 的外键(一个在教练模型上,一个在团队模型上),因此外键不会有唯一的名称。但是,如果 idx 被某个增量值更改,则可以避免这种情况。

var Promise = require('bluebird');

module.exports = {
  up: function (queryInterface, Sequelize) {
    return Promise
      .join(
        queryInterface
          .createTable('sponsor', {
            id: {
              autoIncrement: true,
              primaryKey: true,
              type: Sequelize.INTEGER
            },
            name: {
              type: Sequelize.STRING
            },
          }),
        queryInterface
          .createTable('team', {
            id: {
              autoIncrement: true,
              primaryKey: true,
              type: Sequelize.INTEGER
            },
            name: {
              type: Sequelize.STRING
            },
          }),
        queryInterface
          .createTable('coach', {
            id: {
              autoIncrement: true,
              primaryKey: true,
              type: Sequelize.INTEGER
            },
            name: {
              type: Sequelize.STRING
            },
          })
      )
      .then(function(){
        return queryInterface
          .addColumn('team', 'sponsorId', {
            type: Sequelize.INTEGER,
            references: { model: 'sponsor', key: 'id' }
          })
          .then(function(){
            return queryInterface
              .addColumn('coach', 'sponsorId', {
                type: Sequelize.INTEGER,
                references: { model: 'sponsor', key: 'id' }
              });
          });
      });
  },

  down: function (queryInterface, Sequelize) {
    return queryInterface.dropAllTables(); 
  }
};

完成错误日志转储

{ SequelizeBaseError: ER_DUP_KEY: Can't write; duplicate key in table '#sql-3b7_f1'
    at Query.formatError (/home/usr/me/node_modules/sequelize/lib/dialects/mysql/query.js:175:14)
    at Query._callback (/home/usr/me/node_modules/sequelize/lib/dialects/mysql/query.js:49:21)
    at Query.Sequence.end (/home/usr/me/node_modules/mysql/lib/protocol/sequences/Sequence.js:86:24)
    at Query.ErrorPacket (/home/usr/me/node_modules/mysql/lib/protocol/sequences/Query.js:94:8)
    at Protocol._parsePacket (/home/usr/me/node_modules/mysql/lib/protocol/Protocol.js:280:23)
    at Parser.write (/home/usr/me/node_modules/mysql/lib/protocol/Parser.js:74:12)
    at Protocol.write (/home/usr/me/node_modules/mysql/lib/protocol/Protocol.js:39:16)
    at Socket.<anonymous> (/home/usr/me/node_modules/mysql/lib/Connection.js:109:28)
    at emitOne (events.js:96:13)
    at Socket.emit (events.js:188:7)
    at readableAddChunk (_stream_readable.js:176:18)
    at Socket.Readable.push (_stream_readable.js:134:10)
    at TCP.onread (net.js:551:20)
  name: 'SequelizeDatabaseError',
  message: 'ER_DUP_KEY: Can\'t write; duplicate key in table \'#sql-3b7_f1\'',
  parent: 
   { Error: ER_DUP_KEY: Can't write; duplicate key in table '#sql-3b7_f1'
       at Query.Sequence._packetToError (/home/usr/me/node_modules/mysql/lib/protocol/sequences/Sequence.js:52:14)
       at Query.ErrorPacket (/home/usr/me/node_modules/mysql/lib/protocol/sequences/Query.js:83:18)
       at Protocol._parsePacket (/home/usr/me/node_modules/mysql/lib/protocol/Protocol.js:280:23)
       at Parser.write (/home/usr/me/node_modules/mysql/lib/protocol/Parser.js:74:12)
       at Protocol.write (/home/usr/me/node_modules/mysql/lib/protocol/Protocol.js:39:16)
       at Socket.<anonymous> (/home/usr/me/node_modules/mysql/lib/Connection.js:109:28)
       at emitOne (events.js:96:13)
       at Socket.emit (events.js:188:7)
       at readableAddChunk (_stream_readable.js:176:18)
       at Socket.Readable.push (_stream_readable.js:134:10)
       at TCP.onread (net.js:551:20)
       --------------------
       at Protocol._enqueue (/home/usr/me/node_modules/mysql/lib/protocol/Protocol.js:141:48)
       at Connection.query (/home/usr/me/node_modules/mysql/lib/Connection.js:214:25)
       at /home/usr/me/node_modules/sequelize/lib/dialects/mysql/query.js:40:21
       at Promise._execute (/home/usr/me/node_modules/bluebird/js/release/debuggability.js:300:9)
       at Promise._resolveFromExecutor (/home/usr/me/node_modules/bluebird/js/release/promise.js:481:18)
       at new Promise (/home/usr/me/node_modules/bluebird/js/release/promise.js:77:14)
       at Query.run (/home/usr/me/node_modules/sequelize/lib/dialects/mysql/query.js:39:17)
       at /home/usr/me/node_modules/sequelize/lib/sequelize.js:849:20
       at /home/usr/me/node_modules/retry-as-promised/index.js:40:21
       at Promise._execute (/home/usr/me/node_modules/bluebird/js/release/debuggability.js:300:9)
       at Promise._resolveFromExecutor (/home/usr/me/node_modules/bluebird/js/release/promise.js:481:18)
       at new Promise (/home/usr/me/node_modules/bluebird/js/release/promise.js:77:14)
       at retryAsPromised (/home/usr/me/node_modules/retry-as-promised/index.js:30:10)
       at /home/usr/me/node_modules/sequelize/lib/sequelize.js:848:12
       at tryCatcher (/home/usr/me/node_modules/bluebird/js/release/util.js:16:23)
       at Promise._settlePromiseFromHandler (/home/usr/me/node_modules/bluebird/js/release/promise.js:510:31)
     code: 'ER_DUP_KEY',
     errno: 1022,
     sqlState: '23000',
     index: 0,
     sql: 'ALTER TABLE `coach` ADD `sponsorId` INTEGER, ADD CONSTRAINT `sponsorId_foreign_idx` FOREIGN KEY (`sponsorId`) REFERENCES `sponsor` (`id`);' },
  original: 
   { Error: ER_DUP_KEY: Can't write; duplicate key in table '#sql-3b7_f1'
       at Query.Sequence._packetToError (/home/usr/me/node_modules/mysql/lib/protocol/sequences/Sequence.js:52:14)
       at Query.ErrorPacket (/home/usr/me/node_modules/mysql/lib/protocol/sequences/Query.js:83:18)
       at Protocol._parsePacket (/home/usr/me/node_modules/mysql/lib/protocol/Protocol.js:280:23)
       at Parser.write (/home/usr/me/node_modules/mysql/lib/protocol/Parser.js:74:12)
       at Protocol.write (/home/usr/me/node_modules/mysql/lib/protocol/Protocol.js:39:16)
       at Socket.<anonymous> (/home/usr/me/node_modules/mysql/lib/Connection.js:109:28)
       at emitOne (events.js:96:13)
       at Socket.emit (events.js:188:7)
       at readableAddChunk (_stream_readable.js:176:18)
       at Socket.Readable.push (_stream_readable.js:134:10)
       at TCP.onread (net.js:551:20)
       --------------------
       at Protocol._enqueue (/home/usr/me/node_modules/mysql/lib/protocol/Protocol.js:141:48)
       at Connection.query (/home/usr/me/node_modules/mysql/lib/Connection.js:214:25)
       at /home/usr/me/node_modules/sequelize/lib/dialects/mysql/query.js:40:21
       at Promise._execute (/home/usr/me/node_modules/bluebird/js/release/debuggability.js:300:9)
       at Promise._resolveFromExecutor (/home/usr/me/node_modules/bluebird/js/release/promise.js:481:18)
       at new Promise (/home/usr/me/node_modules/bluebird/js/release/promise.js:77:14)
       at Query.run (/home/usr/me/node_modules/sequelize/lib/dialects/mysql/query.js:39:17)
       at /home/usr/me/node_modules/sequelize/lib/sequelize.js:849:20
       at /home/usr/me/node_modules/retry-as-promised/index.js:40:21
       at Promise._execute (/home/usr/me/node_modules/bluebird/js/release/debuggability.js:300:9)
       at Promise._resolveFromExecutor (/home/usr/me/node_modules/bluebird/js/release/promise.js:481:18)
       at new Promise (/home/usr/me/node_modules/bluebird/js/release/promise.js:77:14)
       at retryAsPromised (/home/usr/me/node_modules/retry-as-promised/index.js:30:10)
       at /home/usr/me/node_modules/sequelize/lib/sequelize.js:848:12
       at tryCatcher (/home/usr/me/node_modules/bluebird/js/release/util.js:16:23)
       at Promise._settlePromiseFromHandler (/home/usr/me/node_modules/bluebird/js/release/promise.js:510:31)
     code: 'ER_DUP_KEY',
     errno: 1022,
     sqlState: '23000',
     index: 0,
     sql: 'ALTER TABLE `coach` ADD `sponsorId` INTEGER, ADD CONSTRAINT `sponsorId_foreign_idx` FOREIGN KEY (`sponsorId`) REFERENCES `sponsor` (`id`);' },
  sql: 'ALTER TABLE `coach` ADD `sponsorId` INTEGER, ADD CONSTRAINT `sponsorId_foreign_idx` FOREIGN KEY (`sponsorId`) REFERENCES `sponsor` (`id`);' }

【问题讨论】:

标签: node.js sequelize.js sequelize-cli


【解决方案1】:

经过一番挖掘,我发现this is a bug 在 v3.21 中引入。有人好心fixed it in the v4 alpha branch 但不在当前版本分支的 v3 中。我为 v3 提交了一个backport of the patch,但它需要一段时间才能发布。

与此同时,我采用了您在上面评论中推荐的手动查询。这并不太痛苦,因为它只影响在现有表上添加/更改查询,而不影响新表上的外键。

文档中的示例有点模糊,因此对于可能偶然发现此问题的其他人,这是我从 umzug 迁移中获得的内容。

up: function (queryInterface, Sequelize) {
    return queryInterface
        .addColumn('operators', 'organization_id', {
            type: Sequelize.INTEGER,
            allowNull: true,
            // This bit will cause the naming conflict
            // references: { model: 'organizations', key: 'id' },
            // onUpdate: 'CASCADE',
            // onDelete: 'RESTRICT'
        })
        .then(() => queryInterface
            .sequelize
            .query('ALTER TABLE `operators` ADD CONSTRAINT `operators_organization_id_foreign_idx` FOREIGN KEY (`organization_id`) REFERENCES `organizations` (`id`) ON DELETE RESTRICT ON UPDATE CASCADE;',
                { type: Sequelize.QueryTypes.RAW }));
},

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-07-21
    • 2016-12-07
    • 1970-01-01
    • 2017-10-24
    • 2015-07-06
    • 2018-08-01
    • 2021-04-01
    • 2018-10-25
    相关资源
    最近更新 更多