web-dev-qa-db-ja.com

SQLAlchemyとAlembicでEnumを使用する方法は?

これが私の投稿モデルです:

class Post(Base):
    __tablename__ = 'posts'

    title = db.Column(db.String(120), nullable=False)
    description = db.Column(db.String(2048), nullable=False)

列挙型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))

DBをアップグレードしようとすると、次のようになります。

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"]
  1. タイプpoststatusがDBレベルで自動的に作成されなかったのはなぜですか?同様の移行では、そうでした。
  2. server_defaultオプションを正しく指定するにはどうすればよいですか?既存の行を変更しているため、ORMレベルのデフォルトとDBレベルのデフォルトの両方が必要です。ORMのデフォルトは適用されません。
  3. DBの実際の値が「DRAFT」、「APPROVE」、「PUBLISHED」であるのに、draftなどではないのはなぜですか?名前ではなく、ENUM値があるべきだと思いました。

前もって感謝します。

7
f1nn

DBの実際の値が「ドラフト」、「承認」、「公開」であるのに、ドラフトなどではないのはなぜですか?名前ではなく、ENUM値があるべきだと思いました。

PeterBašistaがすでに述べたように、SQLAlchemyはデータベースで列挙名(DRAFT、APPROVE、PUBLISHED)を使用します。列挙値( "draft"、 "approve"、...)はPython)の任意の型であり、一意であることが保証されていないため(@uniqueが使用されていない限り)、これが行われたと思います。 )。

ただし、SQLAlchemy 1.2.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がDBレベルで自動的に作成されなかったのはなぜですか?同様の移行では、そうでした。

基本的に、アレムビックの制限に直面していると思います。PostgreSQLで列挙型を正しく処理できない場合があります。あなたの場合の主な問題は Autogenerateがpostgresql列挙型#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)
5
Felix Schwarz

私はあなたの質問の3番目の部分にしか答えることができません。

SQLAlchemyのEnum型の documentation は、次のように述べています。

上記では、各要素の文字列名。 「1」、「2」、「3」はデータベースに保持されます。 Python列挙型(ここでは整数で示されています)の値は使用されません;したがって、各列挙型の値は使用できます永続性があるかどうかに関係なく、あらゆる種類のPythonオブジェクトである必要があります。

したがって、SQLAlchemyの設計により、値ではなくEnumnamesがデータベースに永続化されます。

4
Peter Bašista