SequelizeJS迁移:将多个外键添加到相同的模型?

使用sequelize cli实用工具的迁移function时,可以通过添加新列来创build新的外键。 但是,我试图创build多个外键到同一个模型时遇到了一个错误。 通过运行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部分必须被一个实际的数值replace,但是我还没有能够确定在哪里。 我也validation了sequelize在使用db.sync()通过检查另一个数据库中的外键确实增加了索引值。 在该数据库中,外键被命名为_ibfk_1_ibfk_2 ,.., _ibfk_n

  • 有没有人遇到过外键生成器产生相同名称的问题?
  • 有没有人有一个build议,如何可以避免/缓解使用Sequelize迁移?

我使用MySQL作为数据库引擎,但外键名称生成器遵循相同的程序,例如。 postgre,以及我能够解释的续集源代码。

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

脚本模拟教练队伍赞助商赞助的场景。 在这个模型中,我们希望在教练和球队模型中都参考赞助商id。 不幸的是,这将创build两个名为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`);' } 

经过一番挖掘,我发现这是 v3.21中引入的一个bug 。 有人好好修复它在v4 alpha分支但不是在当前版本分支的v3。 我提交了v3 修补程序的补丁 ,但是要等到它发布的时候才会有一段时间。

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

这些例子在文档中有些模糊,所以对于那些可能偶然发现这个问题的其他人来说,这就是我从一个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 })); },