Не обнаружено изменений в автогенерации миграции Alembic с помощью Flask-SQLAlchemy

У меня возникли проблемы с получением Alembic для автогенерации миграции кандидатов из изменений в классы с помощью db.Model (Flask-SQLAlchemy) вместо Base.

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

...
uri = 'mysql://user:[email protected]/dbname?charset=utf8'
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = uri
app.config['SQLALCHEMY_ECHO'] = True
db.init_app(app)
with app.test_request_context():
    target_metadata = db.Model.metadata
    config.set_main_option('sqlalchemy.url', uri)
    if context.is_offline_mode():
        run_migrations_offline()
    else:
        run_migrations_online()
...

Этот подход отлично подходит для drop_all(), create_all() (например, при воссоздании тестового db для модульного тестирования), но в этом случае он, похоже, упадет. Автоматически сгенерированные скрипты версий всегда имеют пустые методы обновления и понижения, например,

def upgrade():
    ### commands auto generated by Alembic - please adjust! ###
    pass
    ### end Alembic commands ###


def downgrade():
    ### commands auto generated by Alembic - please adjust! ###
    pass
    ### end Alembic commands ###

Мои изменения включали переименование столбцов, изменение определений столбцов и т.д., а не только изменения индексов и внешних ключей.

Кто-нибудь там использует Alembic с Flask-SQLAlchemy? Любая идея, где я ошибаюсь?

Спасибо большое!

Ответы

Ответ 1

Alembic не может автоматически определять переименования таблиц или столбцов. По умолчанию он также не будет искать изменения типа столбца, но для этого можно включить параметр compare_type.

Выдержка из документации Alembic:

Autogenerate будет по умолчанию обнаруживать:

  • Добавление таблиц, удаление.
  • Добавление столбцов, удаление.
  • Изменение статуса с нулевым значением в столбцах.

Autogenerate может опционально обнаруживать:

  • Изменение типа столбца. Это произойдет, если вы установите compare_type = True в EnvironmentContext.configure(). Функция работает хорошо в большинстве случаев, но по умолчанию отключена, поэтому ее можно сначала проверить на целевой схеме. Его также можно настроить, передав здесь вызываемый код; Подробнее см. в документации по функциям.
  • Смена сервера по умолчанию. Это произойдет, если вы установите compare_server_default = True в EnvironmentContext.configure(). Эта функция хорошо работает для простых случаев, но не всегда дает точные результаты. Бэкэнд Postgresql будет фактически вызывать значения "обнаруженных" и "метаданных" для базы данных для определения эквивалентности. Эта функция отключена по умолчанию, чтобы ее можно было сначала проверить на целевой схеме. Подобно сопоставлению типов, его также можно настроить, передав вызываемый; Подробнее см. в документации по функциям.

Autogenerate не может обнаружить:

  • Изменение имени таблицы. Они будут отображаться как добавление/падение двух разных таблиц и вместо этого должны быть отредактированы вручную вместо имени.
  • Изменение имени столбца. Подобно изменениям имен таблиц, они обнаруживаются как пара с добавлением/удалением столбца, что совсем не совпадает с изменением имени.
  • Специальные типы SQLAlchemy, такие как Enum при сгенерировании на бэкэнд, который не поддерживает ENUM напрямую - это потому, что представление такого типа в несуществующей базе данных, то есть ограничение CHAR + CHECK, может быть любым типом CHAR + ПРОВЕРКА. Для SQLAlchemy, чтобы определить, что это на самом деле ENUM, было бы только предположением, что-то, как правило, плохая идея. Чтобы реализовать свою собственную функцию "угадывания" здесь, используйте событие sqlalchemy.events.DDLEvents.column_reflect(), чтобы изменить тип SQLAlchemy, переданный для определенных столбцов и, возможно, sqlalchemy.events.DDLEvents.after_parent_attach(), чтобы перехватить нежелательные ограничения CHECK.

Autogenerate can not в настоящее время, но в конечном итоге обнаружит:

  • Отдельные ограничения ограничений, абстракции, такие как CHECK, UNIQUE, FOREIGN KEY - эти аранты еще не реализованы. Прямо сейчас вы получите ограничения в новых таблицах, ограничениях PK и FK для "понижения" до ранее существующей таблицы, а ограничения CHECK, сгенерированные с помощью "схемы схемы SQLAlchemy" типа Boolean, Enum.
  • Добавления индексов, удаления - еще не реализованы.
  • Добавления последовательности, удаления - еще не реализованы.

UPDATE: некоторые элементы из этого последнего списка поддерживаются в релизах Alembic 0.7.x.

Ответ 2

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

Ответ 3

Попробуйте flask-alembic https://github.com/tobiasandtobias/flask-alembic

Я попробовал это вчера. Он отлично работает для меня, кроме операций drop. Они не работают на sqlite (https://bitbucket.org/zzzeek/alembic/issue/21/column-renames-not-supported-on-sqlite).

Как я его использовал. Сначала я использовал python manage.py migrate revision --autogenerate для создания пустых таблиц в sqlite db. Он будет производить такую ​​миграцию

def upgrade():
    ### commands auto generated by Alembic - please adjust! ###
    op.create_table('users_user',
    sa.Column('id', sa.Integer(), nullable=False),
    sa.Column('name', sa.String(length=50), nullable=True),
    sa.Column('email', sa.String(length=120), nullable=True),
    sa.Column('password', sa.String(length=20), nullable=True),
    sa.Column('role', sa.SmallInteger(), nullable=True),
    sa.Column('status', sa.SmallInteger(), nullable=True),
    sa.PrimaryKeyConstraint('id'),
    sa.UniqueConstraint('email'),
    sa.UniqueConstraint('name')
)
### end Alembic commands ###

def downgrade():
    ### commands auto generated by Alembic - please adjust! ###
    op.drop_table('users_user')
    ### end Alembic commands ###

Тогда - python manage.py migrate upgrade head

Затем я добавил новый столбец test = db.Column(db.String(20)) в модель пользователя и выполнил эту команду python manage.py migrate revision --autogenerate -m 'test field at users'

Это привело к такой миграции:

def upgrade():
    ### commands auto generated by Alembic - please adjust! ###
    op.add_column('users_user', sa.Column('test', sa.String(length=20), nullable=True))
    ### end Alembic commands ###


def downgrade():
    ### commands auto generated by Alembic - please adjust! ###
    op.drop_column('users_user', 'test')
    ### end Alembic commands ###

Ответ 4

Если вы найдете нужную вам функцию, это не поддержка в alembic. В этом случае

  • удалите таблицу (или столбец), затем выполните миграцию

  • добавить таблицу (или столбец), а затем снова выполнить миграцию

это также работает с изменением типа перечисления