如何使用AsyncMigration在Vapor中迁移枚举?
使用AsyncMigration迁移枚举(Swift Concurrency实现)
假设你基于Vapor Fluent框架开发,以下是直接用Swift Concurrency替代EventLoopFuture实现枚举迁移的具体方案:
1. 先定义枚举与关联模型
示例枚举及对应数据库模型:
enum UserStatus: String, Codable, CaseIterable { case active case inactive case suspended // 新增枚举值,需通过迁移生效 } final class User: Model, Content { static let schema = "users" @ID(key: .id) var id: UUID? @Field(key: "status") var status: UserStatus init() {} init(id: UUID? = nil, status: UserStatus) { self.id = id self.status = status } }
2. 实现AsyncMigration迁移类
根据不同的枚举变更场景,编写对应的异步迁移逻辑:
场景1:新增枚举值并批量更新数据
如果需要新增枚举值,同时将符合条件的旧数据切换为新值:
struct AddSuspendedUserStatus: AsyncMigration { func prepare(on database: Database) async throws { // PostgreSQL需先更新枚举类型(SQLite无需此步骤) if let pg = database as? PostgreSQLDatabase { try await pg.raw("ALTER TYPE users_status_enum ADD VALUE 'suspended'").run() } // 批量将inactive状态的用户改为suspended try await User.query(on: database) .filter(\.$status == .inactive) .set(\.$status, to: .suspended) .update() } func revert(on database: Database) async throws { // 回滚:先把suspended状态的用户改回inactive try await User.query(on: database) .filter(\.$status == .suspended) .set(\.$status, to: .inactive) .update() // PostgreSQL需删除新增的枚举值(生产环境需谨慎) if let pg = database as? PostgreSQLDatabase { try await pg.raw("ALTER TABLE users ALTER COLUMN status TYPE users_status_enum_old USING status::text::users_status_enum_old").run() try await pg.raw("DROP TYPE users_status_enum").run() try await pg.raw("ALTER TYPE users_status_enum_old RENAME TO users_status_enum").run() } } }
场景2:修改枚举值的数据库存储映射
比如将.inactive的存储值改为disabled:
struct RenameInactiveToDisabledStatus: AsyncMigration { func prepare(on database: Database) async throws { // 先批量更新数据库中的旧值 try await User.query(on: database) .filter(\.$status == .inactive) .set(\.$status, to: .disabled) .update() // PostgreSQL更新枚举类型的取值映射 if let pg = database as? PostgreSQLDatabase { try await pg.raw("ALTER TYPE users_status_enum RENAME VALUE 'inactive' TO 'disabled'").run() } } func revert(on database: Database) async throws { // 回滚数据 try await User.query(on: database) .filter(\.$status == .disabled) .set(\.$status, to: .inactive) .update() // PostgreSQL还原枚举值映射 if let pg = database as? PostgreSQLDatabase { try await pg.raw("ALTER TYPE users_status_enum RENAME VALUE 'disabled' TO 'inactive'").run() } } }
3. 注册迁移
在configure.swift中注册你的异步迁移:
app.migrations.add(AddSuspendedUserStatus()) app.migrations.add(RenameInactiveToDisabledStatus())
关键注意事项
- 数据库差异:SQLite直接存储枚举字符串,无需修改类型;PostgreSQL会生成专属枚举类型,迁移时需额外处理类型变更。
- 回滚逻辑:必须保证数据一致性,比如新增枚举值时,回滚要先恢复数据再删除枚举类型。
- 生产环境:执行迁移前务必备份数据,尤其是修改枚举类型的操作。
内容的提问来源于stack exchange,提问作者Sam Doggett
相关产品推荐
相关产品推荐

