随着时间的推移,模​​型会为Sequelize制作可重现的迁移字符串吗?

时间:2017-05-17 21:07:14

标签: node.js model migration sequelize.js umzug

我正在使用Umzug和Sequelize构建我的第一个迁移系统,用于在后端使用sequelize和express的节点应用程序。

我的问题是随着后端模型随着时间的推移而变化(模型被删除,一些变化,一些被添加),这打破了碰巧使用sequelize模型的旧迁移。例如:

假设我有一个处理“UserStats”的迁移#1。之后的五个版本模型UserStats需要从应用程序中删除,因此删除模型并进行新的迁移以删除该表。

现在尝试启动新的开发环境会中断,因为当新服务器尝试运行所有旧迁移时,它会尝试为第一次迁移找到模型UserStats,但模型不再存在。

因此根本问题是模型版本与app迁移状态不同步。每次迁移都要求sequelize模型看起来就像最初创建迁移时所做的那样。解决这个问题的最佳方法是什么?

3 个答案:

答案 0 :(得分:1)

这里有两个值得了解的想法。两者都是从我在Ruby on Rails中的背景中获得灵感的,所以我已经介绍了Rails如何为上下文做的事情:

  1. 在Rails中,当您设置新数据库时,它并不期望每次迁移都能运行,以使数据库正确。 Rails还维护一个名为schema.rb的文件,该文件保存数据库的当前,完整,最新状态(包括已经运行以使其达到该点的迁移的名称列表)。因此,当您创建新数据库时,它只会读取schema.rb文件中的任何内容,并创建这些表。
  2. 然后,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次迁移。这意味着你总是保留最后几次迁移的记录,以防你需要回滚它们等等。

    1. Rails世界中的另一个常见做法是更直接地应用于上述问题,即在迁移文件中保存模型的副本(即,副本"及时冻结",所以说,并在您的迁移中使用该副本,而不是您的真实"一,可以在轨道上删除。当然,您不必使用所有方法等包含ENTIRE模型的完整副本 - 您可以删除特定迁移所不需要的任何内容。
    2. 这可能会使您的迁移文件变得庞大而且有点混乱,但是谁在乎呢?迁移不会被编辑。他们只跑了一次而且大部分都被遗忘了。因此,如果它们有点混乱并不重要。

      我还没有用续集来尝试第二种方法,但是对于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));