了解 Sequelize 数据库迁移和种子

Posted

技术标签:

【中文标题】了解 Sequelize 数据库迁移和种子【英文标题】:Understanding Sequelize database migrations and seeds 【发布时间】:2018-05-28 21:24:30 【问题描述】:

我正试图深入了解 Sequelize 的迁移以及它们如何与种子(或者一般的迁移和种子)一起工作。 我设置了一切以使迁移正常工作。

首先,让我们创建一个users 表:

// migrations/01-create-users.js
module.exports = 
  up: (queryInterface, Sequelize) => 
    return queryInterface.createTable("Users", 
      id: 
        allowNull: false,
        autoIncrement: true,
        primaryKey: true,
        type: Sequelize.INTEGER
      ,
      email: 
        type: Sequelize.STRING
      ,
      createdAt: 
        allowNull: false,
        type: Sequelize.DATE,
        defaultValue: Sequelize.literal('CURRENT_TIMESTAMP')
      ,
      updatedAt: 
        type: Sequelize.DATE
      
    );
  ,
  down: (queryInterface, Sequelize) => 
    return queryInterface.dropTable("Users");
  
;

很好。如果我想播种(管理员)用户,我可以这样做:

// seeders/01-demo-user.js
module.exports = 
  up: (queryInterface, Sequelize) => 
    return queryInterface.bulkInsert(
      "Users",
      [
        
          email: "demo@demo.com"
        
      ],
      
    );
  ,

  down: (queryInterface, Sequelize) => 
    return queryInterface.bulkDelete("Users", null, );
  
;

然后为了让魔法发生,我会这样做:

$ sequelize db:migrate

这会在数据库中创建users 表。运行迁移后,下一步是播种,因此:

$ sequelize db:seed:all

Tataa,现在我在 users 数据库中有一个用户。太好了。

但现在我想将firstname 添加到users 表中,所以我必须添加另一个迁移:

// migrations/02-alter-users.js
module.exports = 
  up: (queryInterface, Sequelize) => 
    return queryInterface.addColumn("Users", "firstname", 
      type: Sequelize.STRING
    );
  ,
  down: (queryInterface, Sequelize) => 
    return queryInterface.removeColumn("Users", "firstname");
  
;

再次运行迁移只会运行第二个迁移,因为它保存在第一个已执行的数据库中。但默认情况下,sequelize 会重新运行所有播种机。那么我应该调整seeders/01-demo-user.js 还是更改默认行为并将播种机存储在数据库中并创建一个仅更新firstname 的新播种机?

如果firstname 不能是null,那么先运行迁移然后旧版本的seeders/01-demo-user.js 会抛出错误,因为firstname 不能是null

重新运行播种器会导致另一个问题:已经有一个用户使用demo@demo.com 电子邮件。第二次运行它会复制用户。还是我必须在播种机中检查类似的东西?

以前,我只是在迁移中添加了用户帐户,因此我可以确定何时将其添加到数据库以及何时必须更新它。但是有人告诉我我做错了,我必须使用播种机来完成这样的任务。

非常感谢任何帮助/见解。

【问题讨论】:

你能解决这个问题吗?我也有同样的问题。 并非如此。现在,如果我正在使用播种机,我也将它们存储在运行它们的数据库中,这样它们就不会再次运行。 【参考方案1】:

在我看来,播种机是一种只运行一次的东西,而迁移是你不断地逐层添加到数据库结构中的东西。

我会使用播种器来填充一些查找或其他很可能不会更改的数据,或测试数据。在sequelizedocs 中说,“种子文件是数据中的一些更改,可用于使用示例数据或测试数据填充数据库表。”

如果您想在数据结构已更改时进行一些动态数据更新,您可以在需要时直接在迁移中运行原始查询。因此,例如,如果您在 up 方法中添加了一些列,您可以根据您的业务逻辑更新数据库中的行,例如:

// Add column, allow null at first
await queryInterface.addColumn("users", "user_type", 
    type: Sequelize.STRING,
    allowNull: true
);

// Update data
await queryInterface.sequelize.query("UPDATE users SET user_type = 'simple_user' WHERE is_deleted = 0;");

// Change column, disallow null
await queryInterface.changeColumn("users", "user_type", 
    type: Sequelize.STRING,
    allowNull: false
);

在 Google 网上论坛中也有一个有趣的discussion 关于这个主题。希望对你有帮助。

【讨论】:

这应该是公认的答案。只是遇到了同样的困惑。完美地说。谢谢 The Sequelize docs 在声明中混淆了这个问题:“要管理所有数据迁移,您可以使用播种机”。这根本不是真的。有很多原因应该更新迁移文件中的数据,而不是种子。在已建立的应用程序中,迁移文件中的数据更新似乎比播种机更频繁。 如果我编写一些迁移和播种器,它依赖于这些迁移然后添加更多迁移,使播种器对新列无效,我有点困惑......当然,我可以更新新迁移中的数据,但是如果我想创建一个新的数据库会发生什么?新的迁移依赖于首先运行的播种机......我认为真正应该发生的是迁移和播种机的特定顺序。【参考方案2】:

根据我的经验,迁移会改变结构。播种机...种子数据。最近我在一个没有配置播种机的项目上。 https://sequelize.org/master/manual/migrations.html#seed-storage。这将允许您设置一个文件,这样您的数据就不会被多次播种。迁移配置也在那里。

【讨论】:

【参考方案3】:

您好,试试 bulkUpdate...

'use strict';

module.exports = 
  up: (queryInterface, Sequelize) =>  queryInterface.addColumn(
        'Users',
        'user_type',
        
          type: Sequelize.STRING,
          allowNull: false,
        
      ).then(()=>
        queryInterface.bulkUpdate('Users', 
            user_type: 'simple_user',
        , 
            is_deleted : 0,
        )
      ),

  down: (queryInterface, Sequelize) => 
    queryInterface.removeColumn('Users', 'user_type')
;

【讨论】:

以上是关于了解 Sequelize 数据库迁移和种子的主要内容,如果未能解决你的问题,请参考以下文章

更改数据库模型 - Sequelize

如何在 Docker 中运行 Sequelize 迁移

Sequelize 同步与迁移

heroku sequelize:找不到命令

Sequelize:带有关联的种子

使用 sequelize 和 typescript 进行自动迁移