You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 17:36:06