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_idx
的idx
部分必须被一个实际的数值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 })); },