开启sql_safe_update的MariaDB中Django回滚迁移报错求助
问题
在MariaDB开启sql_safe_update模式的情况下,回滚Django迁移时遇到报错。执行的回滚SQL语句为:
ALTER TABLE `user` DROP COLUMN `pin_code`;
收到Django返回的错误:
MySQLdb.OperationalError: (1175, 'You are using safe update mode and you tried to update a table without a WHERE that uses a KEY column')
由于MySQL DDL语句不支持事务,列已被删除但迁移回滚失败,数据库处于不一致状态,使用--fake参数仍报相同错误。
请问:
- 能否让Django仅在迁移期间临时关闭
sql_safe_update?(无法全局关闭,数据库管理员不同意) - 为何删除列需要WHERE语句?
解答
一、临时在迁移期间关闭sql_safe_update
完全可以通过自定义Django迁移操作实现,不用全局修改数据库配置:
- 要么新建一个空迁移文件,要么修改现有迁移的回滚逻辑,在操作里先执行
SET sql_safe_updates=0;关闭安全模式,执行完删列的DDL后再恢复sql_safe_updates=1;。 - 用Django的
RunSQL操作就能实现,示例代码如下:
from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ ('你的应用名', '上一个迁移文件的名称'), ] operations = [ # 临时关闭安全更新模式 migrations.RunSQL( "SET sql_safe_updates=0;", reverse_sql="SET sql_safe_updates=0;", ), # 执行删列操作,回滚时添加列(注意字段类型要和原字段一致) migrations.RunSQL( "ALTER TABLE `user` DROP COLUMN `pin_code`;", reverse_sql="ALTER TABLE `user` ADD COLUMN `pin_code` VARCHAR(6);", # 这里根据实际字段类型调整 ), # 恢复安全更新模式 migrations.RunSQL( "SET sql_safe_updates=1;", reverse_sql="SET sql_safe_updates=1;", ), ]
- 针对你现在已经失败的情况:先手动确认列的状态(已经被删除的话),然后用
python manage.py migrate --fake 应用名 迁移文件名标记该迁移为已完成,避免重复执行错误操作。
二、为什么删除列会触发safe update检查
这其实是MariaDB/MySQL的一个设计缺陷或者小bug:sql_safe_update本来是用来限制UPDATE/DELETE这类没有WHERE条件、或者WHERE没用到索引的DML语句,但部分版本里会错误地把ALTER TABLE DROP COLUMN这类DDL也纳入检查范围。本质上删列根本不需要WHERE,纯粹是数据库的误判导致了这个错误。
内容的提问来源于stack exchange,提问作者Divick
相关产品推荐
相关产品推荐

