Rails迁移 - 暂时忽略外键约束?

时间:2016-12-06 22:07:57

标签: mysql ruby-on-rails foreign-keys ruby-on-rails-5 rails-migrations

我试图将表格<tag><innertag>some value</innertag></tag>字段更改为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` 列,并修复外键。那么,有没有办法忽略迁移的约束?

4 个答案:

答案 0 :(得分:2)

class ClipBoardCopier extends StatelessWidget { @override Widget build(BuildContext context) { List<String> data = ['Hello', 'Flutter']; return ListView.builder( itemCount: data.length, shrinkWrap: true, itemBuilder: (context, index) { return ListTile( title: Text('ListTile #$index'), onTap: (){ Clipboard.setData(ClipboardData(text: data[index])); }, ); }, ); } } Github)。至少需要Rails 4.2。

P.S。

我发现上面的代码很容易出错。对我而言,主要原因是缺乏针对此类关键任务的测试。来自“ Thoughtbot”的article很棒,涉及将数据迁移与结构迁移混合在一起的其他问题。

我会考虑创建单独的数据迁移(当然会包含测试)。 Example

答案 1 :(得分:1)

我的最终代码

ifunless语句是因为我正在逐步编写和测试(失败的迁移仍然会产生持久影响)。最重要的是删除外键然后将它们添加回去(删除键不会删除数据库中的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

答案 2 :(得分:0)

在mysql上,您可以这样做:

begin
  ActiveRecord::Base.connection.execute 'SET FOREIGN_KEY_CHECKS=0;'
  # Your statements
  ...
ensure
  ActiveRecord::Base.connection.execute 'SET FOREIGN_KEY_CHECKS=1;'
end

答案 3 :(得分:0)

如果您混合使用mysql-sqlite环境,则可以执行以下操作:

class AddUuidToProjects < ActiveRecord::Migration[5.0]
  def up
    ActiveRecord::Base.connection.execute 'SET FOREIGN_KEY_CHECKS=0;' if ActiveRecord::Base.connection.adapter_name == 'Mysql2'
    # Your code
    # ...
    ActiveRecord::Base.connection.execute 'SET FOREIGN_KEY_CHECKS=1;' if ActiveRecord::Base.connection.adapter_name == 'Mysql2'
  end

  def down
    ActiveRecord::Base.connection.execute 'SET FOREIGN_KEY_CHECKS=0;' if ActiveRecord::Base.connection.adapter_name == 'Mysql2'
    # Your code
    # ...
    ActiveRecord::Base.connection.execute 'SET FOREIGN_KEY_CHECKS=1;' if ActiveRecord::Base.connection.adapter_name == 'Mysql2'
  end
end