是否可以使用 sequelize 在迁移中更新 postgresql 数据库中的现有数据

Is it possible to update existing data in postgresql database in a migration using sequelize

我正在尝试使用 sequelize 迁移向模型添加字段,并且需要从 'up' 迁移中更新数据。但是,调用数据库以更新迁移中的数据不会完成或引发错误,它们只是挂起。

我正在尝试将 registrationType 字段添加到我数据库中的现有模型。该字段不应为空,因此我需要添加 'allowNull: false' 属性。尚未设置 registrationType 的旧注册应使用模型中已存在的数据更新为正确的类型。为此,我需要访问模型中的 ID 字段,获取链接对象(指向某个位置的注册链接,其中包含 locationtype)并使用它来确定 registrationType。我将代码添加到迁移中,就好像它是更新某些数据的正常数据库操作一样,但这些调用不会 return 或引发错误。

我不能(也不想)使用默认值,因为应该根据现有数据为每个注册确定该值(添加默认值会使 allowNull 属性过时)。 我的方法: - 添加没有 'allowNull' 约束的列(使用 addColumn) - 更新所有现有数据 - 添加 'allowNull' 约束(使用 changeColumn)

"use strict";

/*const db = require("../models");
const Registration = db.Registration;
const Site = db.Site;
const Location = db.Location;
*/
const REGISTATION_MODEL = "Registrations";
module.exports = {
    up: async (queryInterface, Sequelize) => {
        const transaction = await queryInterface.sequelize.transaction();

        try {
            const Registration = await queryInterface.sequelize.import(
                "../models/registration.js"
            );
            const Site = await queryInterface.sequelize.import(
                "../models/site.js"
            );
            const Location = await queryInterface.sequelize.import(
                "../models/location.js"
            );

            await queryInterface.addColumn(
                REGISTATION_MODEL,
                "registrationType",
                {
                    type: Sequelize.STRING
                },
                { transaction }
            );

            console.log(
                " * Column added, going to update existing registrations."
            );
            const registrations = await Registration.findAll();
            console.log(
                `\tFound ${registrations.length} registrations to be updated.`
            );
            for await (const registration of registrations) {
                const site = await Site.findByPk(registration.SiteId);
                const location = await Location.findByPk(site.LocationId);
                await registration.update(
                    {
                        registrationType: location.locationType
                    },
                    { transaction }
                );
            }

            console.log(`\tUpdated ${registrations.length} registrations.`);
            console.log(" * Adding 'allowNull:false' to field.");
            //allowNull: false
            await queryInterface.changeColumn(
                REGISTATION_MODEL,
                "registrationType",
                { type: Sequelize.STRING, allowNull: false },
                { transaction: t }
            );
            await transaction.commit();
        } catch (ex) {
            await transaction.rollback();
            console.error("Something went wrong: ", ex);
        }
    },

    down: (queryInterface, Sequelize) => {
        return queryInterface.removeColumn(
            REGISTATION_MODEL,
            "registrationType"
        );
    }
};

输出:

Loaded configuration file "config/config.json".
Using environment "development".
== 20191107134514-add-registration-types: migrating =======
 * Column added, going to update existing registrations.

然后挂起。

此处显示的代码不会产生任何错误,也不会产生任何输出。我添加了 console.log 语句,并再次 运行 迁移,这表明代码在第一次 findAll() 调用时挂起。 谁能告诉我该怎么做?

更新: 正如 chat 中所讨论的,我们需要将 addColumn、changeColumn 和更新查询分离到单独的文件中。由于我们不能 运行 所有这些都在同一个事务下,因为它们影响相同的 tables.

如果您想 await 在迁移函数中,您需要使 up/down 函数异步并且 运行 您的事务也使用 await 和 async:

以下是仅用于更新 table 的迁移文件。为 addColumn 和 changeColumn 创建单独的迁移。 运行 他们的顺序是:

  1. 添加列
  2. 更新Table
  3. 更改列

迁移 table 更新:

up: async (queryInterface, Sequelize) => {
    // declare transaction outside try catch so it is available in both
    const transaction = await queryInterface.sequelize.transaction()
    try {
        // No need to use transactions for read operations
        const registrations = await Registration.findAll()

        // using for...of loop which supports awaiting inside it
        for await (const registration of registrations) {
            const site = await Site.findByPk(registration.SiteId)
            const location = await Location.findByPk(site.LocationId)

            // Make sure to await on all sequelize methdos
            await registration.update({ registrationType: location.locationType })
        }

        // Commit transaction if no error occurs
        await transaction.commit()
    } catch (error) {
        // Rollback transaction if error occurs
        await transaction.rollback()
        console.error("Something went wrong: ", ex)
    }
}