Не обнаружено изменений в автогенерации миграции 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. В этом случае
-
удалите таблицу (или столбец), затем выполните миграцию
-
добавить таблицу (или столбец), а затем снова выполнить миграцию
это также работает с изменением типа перечисления