Изменение поля Enum с использованием Alembic

Как добавить элемент в поле Enum в миграции alembic при использовании версии PostgreSQL старше 9.1 (которая добавляет ALTER TYPE для перечислений)? Этот вопрос SO объясняет прямой процесс, но я не совсем уверен, как лучше всего перевести его с помощью alembic.

Это то, что у меня есть:

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 при обновлении, что практически ничего не делает.

5 Solutions collect form web for “Изменение поля Enum с использованием Alembic”

Я решил попытаться как можно более внимательно следить за подходом 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 .

Начиная с Postgres 9.1 добавление нового значения в перечисление может быть выполнено с помощью инструкции ALTER TYPE . Это осложняется тем, что это невозможно сделать в транзакции . Однако это можно обойти, совершив транзакцию alembic, см. Здесь .

У меня были проблемы с использованием более старого, более подробного решения, потому что Postgres не смог автоматически преобразовать значение по умолчанию для столбца.

Я использовал немного более простой подход с меньшими шагами, чем принятый ответ, на котором я основывал это. В этом примере я буду притворяться, что указанное перечисление называется «status_enum», потому что в принятом ответе использование «status» для столбца и enum меня путало.

 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) 

В прямом SQL это будет работать для Postgres, если порядок вещей в вашем перечислении не должен быть таким, как указано выше:

 ALTER TYPE status ADD value 'output_limit_exceeded' after 'timed_out'; 

У меня была та же проблема, что и при переносе типа столбца в другой. Я использую следующие требования:

 Alembic==0.9.4 SQLAlchemy==1.1.12 

Вы можете указать аргумент postgresql_using как kwarg of alembic.op.alter_column .

 from alembic import op import sqlalchemy as types op.alter_column( table_name='my_table', column_name='my_column', type_=types.NewType, # allows to use postgresql USING postgresql_using="my_column::PostgesEquivalentOfNewType", ) 

Надеюсь, это поможет.

  • alembic и получение последнего вставленного значения
  • Alembic: alembic revision говорит об ошибке импорта
  • Flask-SQLAlchemy Lower Case Index - пропуск функциональности, не поддерживаемый отражением SQLAlchemy
  • Alembic - sqlalchemy не обнаруживает существующие таблицы
  • Интеграция Alembic с SQLAlchemy
  • Alembic SqlAlchemy Postgres «NameError: имя« String »не определено», пытаясь добавить поля Array (String)
  • sqlalchemy: alembic bulk insert failed: объект 'str' не имеет атрибута '_autoincrement_column'
  • Изменить первичный ключ в Alembic?
  •  
    Interesting Posts for Van-Lav

    Независимый от файловой системы способ использования glob.glob и регулярных выражений с именами файлов Unicode в Python

    Могу ли я использовать Python 3 super () в Python 2.5.6?

    Что такое питонический способ дифференцирования строки и списка?

    Как обрабатывать тайм-аут urllib в Python 3?

    Анализ по электронной почте: TypeError: parse () принимает не менее 2 аргументов (2 данных)

    Являются ли глобальные переменные потоками безопасными в колбе?

    matplotlib: объединить разные цифры и поместить их в единый подзаголовок, в котором используется общая легенда

    Словари словарей объединяются

    Twisted + SQLAlchemy и лучший способ сделать это

    Преобразование списка в * args в Python

    Как правильно выравнивать содержимое столбцов в простых таблицах reStructuredText?

    Как установить png-модуль в python

    Python Pandas добавляет строки на основе отсутствующих последовательных значений в таймсерах

    Локальный охват, выходящий за рамки

    Захват повторяющихся подшаблонов в регулярном выражении Python

    Python - лучший язык программирования в мире.