如何在SQLAlchemy中添加Enum字段?迁移时遇ProgrammingError
问题描述
我需要为User模型添加user_type字段,但执行alembic upgrade head时始终遇到sqlalchemy.exc.ProgrammingError错误。
models/user.py
class UserType(enum.Enum): USER = "USER" SELLER = "SELLER" ADMIN = "ADMIN" class User(ModelBase): __tablename__ = "user" username = Column(String(64), nullable=False) email = Column(String(64), nullable=False) phone_number = Column(String(32), unique=True, nullable=False) password = Column(String(128)) avatar = Column(JSONB, nullable=True) address = Column(String(128), nullable=True) user_type = Column(Enum(UserType, name="user_type"), default=UserType.USER) # 新增字段
自动生成的alembic迁移文件
def upgrade() -> None: # ### commands auto generated by Alembic - please adjust! ### op.add_column('user', sa.Column('user_type', sa.Enum('USER', 'SELLER', 'ADMIN', name='user_type'), nullable=True)) # ### end Alembic commands ### def downgrade() -> None: # ### commands auto generated by Alembic - please adjust! ### op.drop_column('user', 'user_type') # ### end Alembic commands ###
执行命令后报错:
sqlalchemy.exc.ProgrammingError: (sqlalchemy.dialects.postgresql.asyncpg.ProgrammingError) <class 'asyncpg.exceptions.UndefinedObjectError'>: type "user_type" does not exist [SQL: ALTER TABLE "user" ADD COLUMN user_type user_type]
问题原因与解决方法
原因
PostgreSQL使用枚举类型时,必须先在数据库中创建枚举类型,才能在表字段中引用。自动生成的迁移文件直接尝试添加字段,但此时user_type枚举类型尚未创建,因此触发报错。
解决步骤
- 修改迁移文件的
upgrade函数,先创建枚举类型,再添加字段:
def upgrade() -> None: # 先创建user_type枚举类型,checkfirst避免重复创建报错 user_type_enum = sa.Enum('USER', 'SELLER', 'ADMIN', name='user_type') user_type_enum.create(op.get_bind(), checkfirst=True) # 添加字段,设置server_default让已有数据自动填充默认值 op.add_column('user', sa.Column('user_type', user_type_enum, nullable=True, server_default='USER'))
- 修改
downgrade函数,删除字段后清理枚举类型(更规范的回滚操作):
def downgrade() -> None: op.drop_column('user', 'user_type') # 删除枚举类型,checkfirst避免不存在时报错 user_type_enum = sa.Enum('USER', 'SELLER', 'ADMIN', name='user_type') user_type_enum.drop(op.get_bind(), checkfirst=True)
- 重新执行
alembic upgrade head即可完成迁移。
补充说明
- 如果需要
user_type字段非空,可在本次迁移完成后,再生成一次迁移将字段改为nullable=False,避免已有数据为空导致的报错。
内容的提问来源于stack exchange,提问作者haku
相关产品推荐
相关产品推荐

