67

9.1より古いバージョンのPostgreSQL(列挙型にALTER TYPEを追加する)を使用している場合、アレムビック移行でEnumフィールドに要素を追加するにはどうすればよいですか?このSOの質問は直接のプロセスを説明していますが、アランビックを使用してそれをどのように翻訳するのが最善かはよくわかりません。

これは私が持っているものです:

new_type = sa.Enum('nonexistent_executable', 'output_limit_exceeded',
                   'signal', 'success', 'timed_out', name='status')
old_type = sa.Enum('nonexistent_executable', 'signal', 'success', 'timed_out',
                   name='status')
tcr = sa.sql.table('testcaseresult',
                   sa.Column('status', new_type, nullable=False))


def upgrade():
    op.alter_column('testcaseresult', u'status', type_=new_type,
                    existing_type=old_type)


def downgrade():
    op.execute(tcr.update().where(tcr.c.status==u'output_limit_exceeded')
               .values(status='timed_out'))
    op.alter_column('testcaseresult', u'status', type_=old_type,
                    existing_type=new_type)

残念ながら、上記はALTER TABLE testcaseresult ALTER COLUMN status TYPE statusアップグレード時にのみ生成され、基本的には何も実行されません。

4

14 に答える 14

58

私は、 postgres のアプローチを可能な限り直接的に踏襲することに決め、次のような移行を思いつきました。

from alembic import op
import sqlalchemy as sa

old_options = ('nonexistent_executable', 'signal', 'success', 'timed_out')
new_options = sorted(old_options + ('output_limit_exceeded',))

old_type = sa.Enum(*old_options, name='status')
new_type = sa.Enum(*new_options, name='status')
tmp_type = sa.Enum(*new_options, name='_status')

tcr = sa.sql.table('testcaseresult',
                   sa.Column('status', new_type, nullable=False))


def upgrade():
    # Create a tempoary "_status" type, convert and drop the "old" type
    tmp_type.create(op.get_bind(), checkfirst=False)
    op.execute('ALTER TABLE testcaseresult ALTER COLUMN status TYPE _status'
               ' USING status::text::_status')
    old_type.drop(op.get_bind(), checkfirst=False)
    # Create and convert to the "new" status type
    new_type.create(op.get_bind(), checkfirst=False)
    op.execute('ALTER TABLE testcaseresult ALTER COLUMN status TYPE status'
               ' USING status::text::status')
    tmp_type.drop(op.get_bind(), checkfirst=False)


def downgrade():
    # Convert 'output_limit_exceeded' status into 'timed_out'
    op.execute(tcr.update().where(tcr.c.status==u'output_limit_exceeded')
               .values(status='timed_out'))
    # Create a tempoary "_status" type, convert and drop the "new" type
    tmp_type.create(op.get_bind(), checkfirst=False)
    op.execute('ALTER TABLE testcaseresult ALTER COLUMN status TYPE _status'
               ' USING status::text::_status')
    new_type.drop(op.get_bind(), checkfirst=False)
    # Create and convert to the "old" status type
    old_type.create(op.get_bind(), checkfirst=False)
    op.execute('ALTER TABLE testcaseresult ALTER COLUMN status TYPE status'
               ' USING status::text::status')
    tmp_type.drop(op.get_bind(), checkfirst=False)

alembic はUSING、メソッド内のステートメントを直接サポートしていないalter_tableようです。

于 2013-02-13T03:04:03.253 に答える
26

これに基づいて、受け入れられた回答よりも手順が少ない、少し単純なアプローチを使用しました。この例では、問題の列挙型が「status_enum」と呼ばれているふりをします。受け入れられた回答では、列と列挙型の両方に「status」を使用すると混乱したためです。

from alembic import op 
import sqlalchemy as sa

name = 'status_enum'
tmp_name = 'tmp_' + name

old_options = ('nonexistent_executable', 'signal', 'success', 'timed_out')
new_options = sorted(old_options + ('output_limit_exceeded',))

new_type = sa.Enum(*new_options, name=name)
old_type = sa.Enum(*old_options, name=name)

tcr = sa.sql.table('testcaseresult',
                   sa.Column('status', new_type, nullable=False))

def upgrade():
    op.execute('ALTER TYPE ' + name + ' RENAME TO ' + tmp_name)

    new_type.create(op.get_bind())
    op.execute('ALTER TABLE testcaseresult ALTER COLUMN status ' +
               'TYPE ' + name + ' USING status::text::' + name)
    op.execute('DROP TYPE ' + tmp_name)


def downgrade():
    # Convert 'output_limit_exceeded' status into 'timed_out'                                                                                                                      
    op.execute(tcr.update().where(tcr.c.status=='output_limit_exceeded')
               .values(status='timed_out'))

    op.execute('ALTER TYPE ' + name + ' RENAME TO ' + tmp_name)

    old_type.create(op.get_bind())
    op.execute('ALTER TABLE testcaseresult ALTER COLUMN status ' +
               'TYPE ' + name + ' USING status::text::' + name)
    op.execute('DROP TYPE ' + tmp_name)
于 2015-11-09T20:39:27.777 に答える
1

別の便利な方法を見つけました

op.execute('ALTER TYPE enum_type ADD VALUE new_value')
op.execute('ALTER TYPE enum_type ADD VALUE new_value BEFORE old_value')
op.execute('ALTER TYPE enum_type ADD VALUE new_value AFTER old_value')
于 2020-01-13T09:49:31.367 に答える
-1

このソリューションは理解しやすく、アップグレードとダウングレードの両方で非常にうまく機能します。この回答をより詳細に書きました。

私たちのenum_type見た目が次のようになっているとしましょう:

enum_type = ('some_value_1', 'some_value_2')

新しい列挙型を追加して変更したいenum_typeので、次のようになります。

enum_type = ('some_value_1', 'some_value_2', 'new_value')

これは、次の方法で行うことができます。

from alembic import op


def upgrade():
    op.execute("COMMIT")
    op.execute("ALTER TYPE enum_type ADD VALUE 'new_value'")


def downgrade():
    # Drop 'new_value' from enum_type
    op.execute("ALTER TYPE enum_type RENAME TO enum_type_tmp")

    op.execute("CREATE TYPE enum_type AS ENUM('some_value_1', 'some_value_1')")

    op.execute("DROP TYPE enum_type_tmp")

注: ダウングレード中にenum_typeテーブルで使用している場合は、以下に示すようにダウングレード方法を変更できます。

def downgrade():
    # Drop 'new_value' from enum_type
    op.execute("UPDATE table_name"
               " SET column_name_using_enum_type_value = NULL"
               " WHERE column_name_using_enum_type_value = 'new_value'")    

    op.execute("ALTER TYPE enum_type RENAME TO enum_type_tmp")

    op.execute("CREATE TYPE enum_type AS ENUM('some_value_1', 'some_value_1')")

    op.execute("ALTER TABLE table_name"
               " ALTER COLUMN column_name_using_enum_type_value TYPE enum_type"
               " USING column_name_using_enum_type_value::text::enum_type")

    op.execute("DROP TYPE enum_type_tmp")
于 2020-12-09T18:32:47.207 に答える