我正在尝试将表格的id
字段更改为uuid
这是我的代码:
class AddUuidToProjects < ActiveRecord::Migration[5.0]
def up
add_column :projects, :uuid, :string, limit:36, null: false, first: true
add_column :projects, :old_id, :integer
Project.all.each do |p|
p.update!(old_id: p.id)
end
change_table :projects do |t|
t.remove :id
t.rename :uuid, :id
end
execute "ALTER TABLE projects ADD PRIMARY KEY (id);"
Project.all.each do |p|
# has_one image
Image.find(p.old_id).update!(project: p)
# has_many stories
Story.where(project_id: p.old_id).each do |s|
s.update!(project: p)
end
end
end
...
end
由于外键约束,尝试t.remove :id
时此迁移会中断。错误消息是:
Mysql2::Error: Cannot drop column 'id': needed in a foreign key constraint 'fk_rails_be41fd4bb7' of table 'db_dev.stories': ALTER TABLE `projects` DROP `id`
问题是,如果整个迁移运行,那么我将把id
列与另一个进行交换,并修复外键。那么,有没有办法忽略迁移的约束?
我的最终代码
if
和unless
声明是因为我正在逐步编写和测试(失败的迁移仍然具有持久影响)。最主要的是删除外键然后将它们添加回去(删除键不会删除数据库中的id
字段,只会删除约束。
class AddUuidToProjects < ActiveRecord::Migration[5.0]
def up
# remove constraint
if foreign_key_exists?(:stories, :projects)
say("removing foreign key constraints")
remove_foreign_key "stories", "projects"
remove_foreign_key "images", "projects"
end
# create UUID id column
unless column_exists?(:projects, :id, :string)
say("adding UUID column")
add_column :projects, :uuid, :string, limit:36, null: false, first: true
add_column :projects, :old_id, :integer
Project.all.each do |p|
p.update!(old_id: p.id, uuid: SecureRandom.uuid)
end
change_table :projects do |t|
t.remove :id
t.rename :uuid, :id
end
execute "ALTER TABLE projects ADD PRIMARY KEY (id);"
end
# update foreign keys
if(Image.first.project_id.is_a? Integer)
say("updating foreign keys")
# change foreign key fields to STRING(36)
change_column :images, :project_id, :string, limit:36, null: false
change_column :stories, :project_id, :string, limit:36, null: false
Project.all.each do |p|
# has_one soi
Image.find_by(project: p.old_id).update!(project: p)
# has_many stories
Snippet.where(project_id: p.old_id).each do |s|
s.update!(project: p)
end
end
end
# add constraints back
unless foreign_key_exists?(:stories, :projects)
say("adding foreign key constraints back")
add_foreign_key "stories", "projects"
add_foreign_key "images", "projects"
end
end
在mysql上,你可以这样做:
begin
ActiveRecord::Base.connection.execute 'SET FOREIGN_KEY_CHECKS=0;'
# Your statements
...
ensure
ActiveRecord::Base.connection.execute 'SET FOREIGN_KEY_CHECKS=1;'
end