使用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库中的某个地方,idx
部分_foreign_idx
部分必须在使用db.sync()
初始化模型时替换为实际数值,但我无法识别。我还验证了sequelize在使用db.sync()
时通过检查另一个数据库中的外键确实增加了索引值。在该数据库中,外键被命名为_ibfk_1
,_ibfk_2
,..,_ibfk_n
我使用 MySQL 作为数据库引擎,但外键名称生成器遵循相同的过程,例如。 postgre以及我能够解释续集源代码。
以下迁移将创建三个模型,并使用sequelize cli
中的函数addColumn函数在它们之间创建关系。
该脚本模拟了 coach 和团队都由某些赞助商赞助的场景。在这个模型中,我们想在我们的教练和团队模型中引用赞助商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`);' }
答案 0 :(得分:2)
经过一番挖掘后,我发现v3.21中引入了this is a bug。有人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 }));
},