我正在使用Umzug和Sequelize构建我的第一个迁移系统,用于在后端使用sequelize和express的节点应用程序。
我的问题是随着后端模型随着时间的推移而变化(模型被删除,一些变化,一些被添加),这打破了碰巧使用sequelize模型的旧迁移。例如:
假设我有一个处理“UserStats”的迁移#1。之后的五个版本模型UserStats需要从应用程序中删除,因此删除模型并进行新的迁移以删除该表。
现在尝试启动新的开发环境会中断,因为当新服务器尝试运行所有旧迁移时,它会尝试为第一次迁移找到模型UserStats,但模型不再存在。
因此根本问题是模型版本与app迁移状态不同步。每次迁移都要求sequelize模型看起来就像最初创建迁移时所做的那样。解决这个问题的最佳方法是什么?
答案 0 :(得分:1)
这里有两个值得了解的想法。两者都是从我在Ruby on Rails中的背景中获得灵感的,所以我已经介绍了Rails如何为上下文做的事情:
schema.rb
的文件,该文件保存数据库的当前,完整,最新状态(包括已经运行以使其达到该点的迁移的名称列表)。因此,当您创建新数据库时,它只会读取schema.rb
文件中的任何内容,并创建这些表。然后,rails将在该数据库上运行FUTURE迁移,继续前进。但是那些在最新schema.rb
被创建之前运行的那些 - 好吧,你根本不需要它们。事实上,一旦他们在所需的任何地方运行,您可以根据需要删除它们。
现在,续集并没有这样做(尽管我希望它能做到!)。但是,你可以做的就是经常将整个数据库结构转储为SQL。保存为例如。迁移文件中的20170427051240-initial-structure.sql
,在其包含的最新迁移后加上时间戳。所以,现在你有类似铁路的scehma.rb
。
下一步是:编辑直接在该时间戳之前运行的迁移,以便它所做的就是导入整个数据库结构。像这样:
'use strict';
module.exports = {
up: function(queryInterface, Sequelize) {
if (!['test', 'ci'].includes(process.env.NODE_ENV)) {
// In all other environments, the database will already have been set up, so
// we don't need to import the full structure
return;
}
return queryInterface.sequelize.query(`
BIG FAT STRING OF SQL GOES HERE.
Note: in my app I've actually got it done table by table,
in separate chained promises, but don't remember why.
`)
}
}
行。因此,现在您可以删除之前的所有迁移 - 因为无论如何都要处理之前运行的所有迁移。
有一些注意事项,例如。您可能希望保存20170427051240-initial-structure.sql
,然后等待一两个月,允许另外15次迁移累积,然后执行上述步骤,以便设置新数据库将导入初始结构,因为它是一个月前,在第一次迁移中,然后运行最近的15次迁移。这意味着你总是保留最后几次迁移的记录,以防你需要回滚它们等等。
这可能会使您的迁移文件变得庞大而且有点混乱,但是谁在乎呢?迁移不会被编辑。他们只跑了一次而且大部分都被遗忘了。因此,如果它们有点混乱并不重要。
我还没有用续集来尝试第二种方法,但是对于Rails来说它很适合。
希望其中一些有用!
答案 1 :(得分:0)
感谢Josh的指示。
我的最终解决方案是继续使用Umzug和我一直使用的相同设置,并使用sequelize.query用原始查询替换任何基于模型的查询。
Sequelize为自动格式化结果(如模型(JS对象))提供了一些很好的功能,因此您唯一需要做的就是编写查询。我的插入/更新/删除非常简单。
这允许我使用节点的标准迁移模式,同时仍然具有从app start到present的可重现的迁移历史,而不依赖于更改模型,并且还使用JS进行迁移,而不是使用SQL函数语言操作数据
希望这有用。
答案 2 :(得分:0)
(写一个答案,因为要注释的字符很多)
更多信息供将来的用户使用。
我从joshua.paling的答案中使用与#2相似的模式:在迁移级别创建模型的副本,以获取执行迁移时数据库的外观快照。
我最终遇到了一些错误:
SequelizeAssociationError:您已经在两个中使用了别名子代 单独的协会。别名关联必须具有唯一的别名。
解决方案是清除模型以及每次迁移执行之间的关联。
function clearModels(sequelize) {
Object.keys(sequelize.models).forEach(m => {
Object.keys(sequelize.models[m].associations).forEach(a => {
delete sequelize.models[m].associations[a];
});
delete sequelize.models[m];
});
}
function clearCache(sequelize) {
sequelize.importCache = {};
}
function migrated(sequelize) {
return (name, migration) => {
clearModels(sequelize);
clearCache(sequelize);
};
}
const {umzug, sequelize} = getUmzug();
umzug.on('migrated', migrated(sequelize));
umzug.on('reverted', migrated(sequelize));