Как написать изменение имен столбцов с помощью sqlalchemy-migrate? - PullRequest
10 голосов
/ 05 октября 2011

Я пытаюсь изменить имя столбца. Первая попытка была с этим сценарием:

meta = MetaData()

users = Table('users', meta,
    Column('id', Integer, primary_key=True),
    Column('name', String(50), unique=True),
    Column('email', String(120), unique=True)
    )

def upgrade(migrate_engine):
    meta.bind = migrate_engine
    users.c.id.alter(name='id')

def downgrade(migrate_engine):
    meta.bind = migrate_engine
    users.c.id.alter(name='user_id')

Запуск migrate.py test в моей базе данных dev (sqlite) работает так же, как и обновление и понижение. Но при развертывании его в моей тестовой среде на Heroku (где используется PostgreSQL 8.3) я получаю трассировку при попытке обновить. Суть этого сообщения:

sqlalchemy.exc.ProgrammingError: (ProgrammingError) column "id" does not exist 

Затем я попытался использовать users.c.user_id в методе обновления. Это не удается в обеих средах.

AttributeError: user_id

Обходное решение, которое я сейчас использую, - это скрипт:

meta_old = MetaData()
meta_new = MetaData()

users_old = Table('users', meta_old,
    Column('user_id', Integer, primary_key=True),
    Column('name', String(50), unique=True),
    Column('email', String(120), unique=True)
    )

users_new = Table('users', meta_new,
    Column('id', Integer, primary_key=True),
    Column('name', String(50), unique=True),
    Column('email', String(120), unique=True)
    )

def upgrade(migrate_engine):
    meta_old.bind = migrate_engine
    users_old.c.user_id.alter(name='id')

def downgrade(migrate_engine):
    meta_new.bind = migrate_engine
    users_new.c.id.alter(name='user_id')

Уже рекомендуется практиковать копирование-вставку модели в сценарии sqlalchemy-migrate. Но это лишнее дублирование становится слишком большим для меня. Кто-нибудь знает, как это должно быть сделано. Предполагая, что это ошибка, я хотел бы посоветовать, как высушить обходной путь.

Ответы [ 3 ]

14 голосов
/ 06 октября 2011

Оказывается, есть даже СУХОЕ решение, чем я надеялся. Самоанализ! Вот так:

def upgrade(migrate_engine):
    meta = MetaData(bind=migrate_engine)
    users = Table('users', meta, autoload=True)
    users.c.user_id.alter(name='id')

def downgrade(migrate_engine):
    meta = MetaData(bind=migrate_engine)
    users = Table('users', meta, autoload=True)
    users.c.id.alter(name='user_id')

Работает как шарм!

9 голосов
/ 27 сентября 2017

Этот тоже работает:

from alembic import op
....
def upgrade(migrate_engine):
    op.alter_column('users', 'user_id', new_column_name='id')

def downgrade(migrate_engine):
    op.alter_column('users', 'id', new_column_name='user_id')
1 голос
/ 05 октября 2011

Могу поспорить, что он не может генерировать SQL, потому что ваши ссылки на метаданные перепутаны.Кажется, вы используете два разных объекта метаданных в ваших Table классах, и это действительно не хорошо.Вам нужен только один.Метаданные отслеживают устарелость объектов, нужно ли им выдавать запросы на обновления объектов, ограничения внешнего ключа и т. Д., А также нужно знать обо всех ваших таблицах и связях.

Изменить для использования одного MetaData object, и передайте echo=True вашему вызову sqlalchemy.create_engine, и он напечатает используемый SQL-запрос в стандартный вывод.Попробуйте выполнить этот запрос самостоятельно, войдя в систему с той же ролью (пользователем) в Postgres.Вы можете обнаружить, что это простая проблема с разрешениями.

Относительно вставки копий: я думаю, что у Django есть хорошее соглашение о размещении Table и декларативных классов в их собственном модуле и их импорте.Однако, поскольку вы должны передать объект MetaData фабрике Table, это усложняет ситуацию.Вы можете использовать объект одноэлементных / глобальных метаданных или просто преобразовать их в декларативные.

Некоторое время я решал реализовать функции с одним аргументом, которые возвращали Table объекты с заданными метаданными и кэшировали результат - в результатереализация класса модели синглтона.Тогда я решил, что это глупо, и переключился на декларативное.

...