正确删除并使用 sequelize 创建 ENUM?
Posted
技术标签:
【中文标题】正确删除并使用 sequelize 创建 ENUM?【英文标题】:Drop and create ENUM with sequelize correctly? 【发布时间】:2018-01-08 07:50:20 【问题描述】:如何在迁移中为 Postgres 正确删除然后重新创建 ENUM 类型?例如,此迁移不会删除 enum_Users_status
枚举...因此,任何在创建/更改 status
值后的尝试都会失败。
module.exports =
up: function (queryInterface, DataTypes)
queryInterface.createTable('Users',
//...
status:
type: DataTypes.ENUM,
values: [
'online',
'offline',
],
defaultValue: 'online'
//...
)
,
down: function (queryInterface)
queryInterface.dropTable('Users')
,
最终我确实设法删除了down
中的枚举类型,但随后up
迁移(应该从头开始创建这个status
枚举)失败了,说public.enum_Users_status
枚举类型没有存在..
【问题讨论】:
【参考方案1】:更新:到目前为止,我已经在三个项目中使用了它,所以我决定创建一个 npm 模块:https://www.npmjs.com/package/replace-enum-postgresql。
我制作了一个实用程序来执行此操作,希望对您有所帮助。
utils/replace_enum.js
:
'use strict';
/**
* Since PostgreSQL still does not support remove values from an ENUM,
* the workaround is to create a new ENUM with the new values and use it
* to replace the other.
*
* @param String tableName
* @param String columnName
* @param String defaultValue
* @param Array newValues
* @param Object queryInterface
* @param String enumName - Optional.
*
* @return Promise
*/
module.exports = function replaceEnum(
tableName,
columnName,
defaultValue,
newValues,
queryInterface,
enumName = `enum_$tableName_$columnName`
)
const newEnumName = `$enumName_new`;
return queryInterface.sequelize.transaction((t) =>
// Create a copy of the type
return queryInterface.sequelize.query(`
CREATE TYPE $newEnumName
AS ENUM ('$newValues.join('\', \'')')
`, transaction: t )
// Drop default value (ALTER COLUMN cannot cast default values)
.then(() => queryInterface.sequelize.query(`
ALTER TABLE $tableName
ALTER COLUMN $columnName
DROP DEFAULT
`, transaction: t ))
// Change column type to the new ENUM TYPE
.then(() => queryInterface.sequelize.query(`
ALTER TABLE $tableName
ALTER COLUMN $columnName
TYPE $newEnumName
USING ($columnName::text::$newEnumName)
`, transaction: t ))
// Drop old ENUM
.then(() => queryInterface.sequelize.query(`
DROP TYPE $enumName
`, transaction: t ))
// Rename new ENUM name
.then(() => queryInterface.sequelize.query(`
ALTER TYPE $newEnumName
RENAME TO $enumName
`, transaction: t ))
.then(() => queryInterface.sequelize.query(`
ALTER TABLE $tableName
ALTER COLUMN $columnName
SET DEFAULT '$defaultValue'::$enumName
`, transaction: t ));
);
这是我的示例迁移:
'use strict';
const replaceEnum = require('./utils/replace_enum');
module.exports =
up: (queryInterface, Sequelize) =>
return replaceEnum(
tableName: 'invoices',
columnName: 'state',
enumName: 'enum_invoices_state',
defaultValue: 'created',
newValues: ['archived', 'created', 'paid'],
queryInterface
);
,
down: (queryInterface, Sequelize) =>
return replaceEnum(
tableName: 'invoices',
columnName: 'state',
enumName: 'enum_invoices_state',
defaultValue: 'draft',
newValues: ['archived', 'draft', 'paid', 'sent'],
queryInterface
);
;
【讨论】:
如何添加没有 defaultValue 的枚举? @Vaulstein 你可以省略defaultValue
选项。
嘿@Abel 试过了,没用。将分享错误信息【参考方案2】:
如果您想更改/编辑类型枚举而不丢失数据。这是我的迁移代码。希望对您有所帮助。
queryInterface.changeColumn(
'table_name',
'Column_name',
type: Sequelize.TEXT,
,
),
queryInterface.sequelize.query('drop type enum_tableName_columnName;')
.then(() => queryInterface.changeColumn(
'table_name',
'column_name',
type: Sequelize.ENUM('value1','value2'),
,
)),
【讨论】:
如果您有区分大小写的表/列名称,请记住在枚举名称周围使用“” - 否则您将收到type does not exist
错误。【参考方案3】:
在down
中手动删除 ENUM 对我来说效果很好。
module.exports =
up: function (queryInterface, DataTypes)
queryInterface.createTable('Users',
//...
status:
type: DataTypes.ENUM,
values: [
'online',
'offline',
],
defaultValue: 'online'
//...
)
,
down: function (queryInterface)
return queryInterface.sequelize.transaction(t =>
return Promise.all([
queryInterface.dropTable('Users'),
queryInterface.sequelize.query('DROP TYPE IF EXISTS "enum_Users_status";'),
]);
);
;
【讨论】:
你把所有的桌子都丢在这里了【参考方案4】:详细说明 shakir ullah 的帖子和 comment on github,这对我有用:
module.exports =
up: (queryInterface, Sequelize) =>
// 1. Change the type of the column to string
return queryInterface.changeColumn('Users', 'status',
type: Sequelize.STRING,
)
// 2. Drop the enum
.then(() =>
const pgEnumDropQuery = queryInterface.QueryGenerator.pgEnumDrop('Users', 'status');
return queryInterface.sequelize.query(pgEnumDropQuery);
)
// 3. Create the enum with the new values
.then(() =>
return queryInterface.changeColumn('Users', 'status',
type: Sequelize.ENUM,
values: [
'online',
'offline',
],
defaultValue: 'online'
);
)
,
// Here I made the choice to restore older values but it might not work
// if rows were inserted with the new enum.
// What you want to do then is up to you. Maybe lose the enum and keep
// the column as a string.
down: (queryInterface, Sequelize) =>
// Do as above to restore older enum values
return queryInterface.changeColumn('Users', 'status',
type: Sequelize.STRING,
).then(() =>
const pgEnumDropQuery = queryInterface.QueryGenerator.pgEnumDrop('Users', 'status');
return queryInterface.sequelize.query(pgEnumDropQuery);
).then(() =>
return queryInterface.changeColumn('Users', 'status',
type: Sequelize.ENUM,
values: [
'older',
'values',
],
defaultValue: 'older'
);
)
,
【讨论】:
【参考方案5】:这种方式对我有用:
module.exports =
up: async (queryInterface, Sequelize) =>
await queryInterface.addColumn(
'users',
'status',
type: Sequelize.ENUM,
values: [
'online',
'offline'
],
defaultValue: 'online',
allowNull: false,
)
,
down: async (queryInterface) =>
await queryInterface.removeColumn('users', 'status')
.then(queryInterface.sequelize.query('DROP TYPE IF EXISTS "enum_users_status";'))
;
【讨论】:
以上是关于正确删除并使用 sequelize 创建 ENUM?的主要内容,如果未能解决你的问题,请参考以下文章
Sequelize .update() 不使用正确的位置并更新所有行
从已经定义的模型中获取 Sequelize.js ENUM 值
sequelize migration delete enum col and want that col back occur error