这是我的帖子模型:
class Post(Base):
__tablename__ = 'posts'
title = db.Column(db.String(120), nullable=False)
description = db.Column(db.String(2048), nullable=False)
我想将Enum status
添加到其中。所以,我创建了一个新的枚举:
import enum
class PostStatus(enum.Enum):
DRAFT='draft'
APPROVE='approve'
PUBLISHED='published'
并为模型添加了一个新字段:
class Post(Base):
...
status = db.Column(db.Enum(PostStatus), nullable=False, default=PostStatus.DRAFT.value, server_default=PostStatus.DRAFT.value)
执行FLASK_APP=server.py flask db migrate
后,生成了此类迁移:
def upgrade():
op.add_column('posts', sa.Column('status', sa.Enum('DRAFT', 'APPROVE', 'PUBLISHED', name='poststatus'), server_default='draft', nullable=False))
尝试升级数据库后,我得到了:
sqlalchemy.exc.ProgrammingError: (psycopg2.ProgrammingError) type "poststatus" does not exist
LINE 1: ALTER TABLE posts ADD COLUMN status poststatus DEFAULT 'draf...
^
[SQL: "ALTER TABLE posts ADD COLUMN status poststatus DEFAULT 'draft' NOT NULL"]
poststatus
未自动在DB级别创建?在类似的迁移过程中。server_default
选项?我需要ORM级别的默认值和DB级别的默认值,因为我正在改变现有的行,因此不会应用ORM默认值。draft
等?我认为应该有ENUM值,而不是名字。提前谢谢。
答案 0 :(得分:3)
我只能回答您问题的第三部分。
SQLAlchemy中Enum
类型的documentation指出:
上面,每个元素的字符串名称,例如“一个”,“两个”,“三个”被保存到数据库中; 不使用Python Enum的值(此处表示为整数);因此,每个枚举的值可以是任何类型的Python对象,无论它是否可持久。
因此,通过SQLAlchemy设计,Enum
名称,而不是值被持久保存到数据库中。
答案 1 :(得分:2)
为什么DB中的实际值是'DRAFT','APPROVE','PUBLISHED',而不是草稿等?我以为应该有ENUM值,而不是名称。
正如PeterBašista已经提到的,SQLAlchemy在数据库中使用枚举名称(草稿,批准,发布)。我之所以这样做是因为枚举值(“ draft”,“ approve”,...)在Python中可以是任意类型,并且不能保证它们是唯一的(除非使用@unique
)。
但是,从 SQLAlchemy 1.2.3 开始,Enum
类接受参数values_callable
,该参数可用于在数据库中存储枚举值:
status = db.Column(
db.Enum(PostStatus, values_callable=lambda obj: [e.value for e in obj]),
nullable=False,
default=PostStatus.DRAFT.value,
server_default=PostStatus.DRAFT.value
)
为什么没有在数据库级别自动创建类型poststatus?在类似的迁移中。
我认为基本上您遇到了alembic的局限性:在某些情况下,它将无法正确处理PostgreSQL上的枚举。我怀疑您遇到的主要问题是Autogenerate doesn't correctly handle postgresql enums #278。
我注意到,如果我使用alembic.op.create_table
,则类型创建正确,所以我的解决方法基本上是:
enum_type = SQLEnum(PostStatus, values_callable=lambda enum: [e.value for e in enum])
op.create_table(
'_dummy',
sa.Column('id', Integer, primary_key=True),
sa.Column('status', enum_type)
)
op.drop_table('_dummy')
c_status = Column('status', enum_type, nullable=False)
add_column('posts', c_status)
答案 2 :(得分:1)
如果使用的是PostgreSQL,请使用以下函数示例:
from sqlalchemy.dialects import postgresql
from ... import PostStatus
from alembic import op
import sqlalchemy as sa
def upgrade():
post_status = postgresql.ENUM(PostStatus, name="status")
post_status.create(op.get_bind(), checkfirst=True)
op.add_column('posts', sa.Column('status', post_status))
def downgrade():
post_status = postgresql.ENUM(PostStatus, name="status")
post_status.drop(op.get_bind())
答案 3 :(得分:-2)
来自官方文档:https://docs.python.org/3/library/enum.html#creating-an-enum
import enum
class PostStatus(enum.Enum):
DRAFT = 0
APPROVE = 1
PUBLISHED = 2
根据这个:
class Post(Base):
...
status = db.Column(db.Integer(), nullable=False, default=PostStatus.DRAFT.value, server_default=PostStatus.DRAFT.value)
1)PostStatus
不是数据库模型,它只是一个包含状态ID的类;
2)没关系
3)您不必在DB中存储状态字符串,最好使用ID