Как удалить ограничение внешнего ключа в SQLAlchemy?
Я использую SQLAlchemy Migrate для отслеживания изменений в базе данных и столкнулся с проблемой удаления внешнего ключа. У меня есть две таблицы, t_new - это новая таблица, а t_exists - это существующая таблица. Мне нужно добавить t_new, затем добавить внешний ключ к t_exists. Тогда я должен иметь возможность отменить операцию (вот где у меня проблемы).
t_new = sa.Table("new", meta.metadata,
sa.Column("new_id", sa.types.Integer, primary_key=True)
)
t_exists = sa.Table("exists", meta.metadata,
sa.Column("exists_id", sa.types.Integer, primary_key=True),
sa.Column(
"new_id",
sa.types.Integer,
sa.ForeignKey("new.new_id", onupdate="CASCADE", ondelete="CASCADE"),
nullable=False
)
)
Это отлично работает:
t_new.create()
t_exists.c.new_id.create()
Но это не так:
t_exists.c.new_id.drop()
t_new.drop()
Попытка удалить столбец внешнего ключа выдает ошибку: 1025, "Ошибка при переименовании файла".\ My_db_name\#sql-1b0_2e6 "в".\ My_db_name \ существующие "(номер ошибки: 150)"
Если я сделаю это с сырым SQL, я могу удалить внешний ключ вручную, а затем удалить столбец, но я не смог выяснить, как удалить внешний ключ с помощью SQLAlchemy? Как я могу удалить внешний ключ, а затем столбец?
4 ответа
Вы можете сделать это с sqlalchemy.migrate.
Чтобы заставить его работать, мне пришлось создать ограничение внешнего ключа явно, а не имплицитно с помощью Column('fk', ForeignKey('fk_table.field')):
Увы, вместо этого:
p2 = Table('tablename',
metadata,
Column('id', Integer, primary_key=True),
Column('fk', ForeignKey('fk_table.field')),
mysql_engine='InnoDB',
)
сделай это:
p2 = Table('tablename',
metadata,
Column('id', Integer, primary_key=True),
Column('fk', Integer, index=True),
mysql_engine='InnoDB',
)
ForeignKeyConstraint(columns=[p2.c.fk], refcolumns=[p3.c.id]).create()
Тогда процесс удаления выглядит так:
def downgrade(migrate_engine):
# First drop the constraint
ForeignKeyConstraint(columns=[p2.c.fk], refcolumns=[p3.c.id]).drop()
# Then drop the table
p2.drop()
Я смог сделать это, создав отдельный экземпляр метаданных и используя Session.execute() для запуска необработанного SQL. В идеале, было бы решение, которое использует исключительно sqlalchemy, поэтому мне не пришлось бы использовать специфичные для MySQL решения. Но на данный момент я не знаю о таком решении.
Я считаю, что вы можете достичь этого с помощью SQLAlchemy-Migrate. Обратите внимание, что ForeignKey находится в изолированном столбце. ForeignKeyConstraint находится на уровне таблицы и связывает столбцы вместе. Если вы посмотрите на объект ForeignKey в столбце, то увидите, что он ссылается на ForeignKeyConstraint.
Я не смог бы проверить эту идею, потому что две базы данных, которые я использую, MS SQL не поддерживается SqlAlchemy-Migrate, а sqlite не поддерживает "изменение таблицы" для ограничений. Я заставил SQLAlchemy попытаться удалить FK с помощью удаления ограничения ссылок в таблице sqlite, чтобы оно выглядело хорошо. YMMV.
Ну, вы можете достичь этого в sqlalchemy: просто drop()
все ограничения перед вами drop()
столбец (теоретически у вас может быть несколько ограничений):
def drop_column(column):
for fk in column.table.foreign_keys:
if fk.column == column:
print 'deleting fk ', fk
fk.drop()
column.drop()
drop_column(t_exists.c.new_id)