Rails更改列类型并更新列值

时间:2014-10-13 16:04:43

标签: ruby-on-rails postgresql activerecord ruby-on-rails-4 rails-activerecord

我有一个名为Users的表,其列为Active,类型为boolean。我很快就需要将该列的类型更改为字符串。我还需要将Active的所有列值更改为:true到" active"和:false到"不活动"。

要更改我要使用的列类型 Change a column type from Date to DateTime during ROR migration

class ChangeColumnTypeInUsers < ActiveRecord::Migration
  def up
    change_column :users, :active, :string
  end

  def down
    change_column :users, :active, :boolean
  end
end

如何更新列值以便类型更改不会破坏它?它会自动转换:true到&#34; true&#34;?

3 个答案:

答案 0 :(得分:3)

使您的迁移完美无缺的简单方法是重命名您的boolean:active列。添加新列。运行SQL更新,然后删除未使用的列。所有这些都可以在同一次迁移中完成。像这样。不包括向下迁移,所以使用你自己的危险:)。

class ChangeColumnTypeInUsers < ActiveRecord::Migration
  def up
    rename_column :users, :active, :active_boolean
    add_column :users, :active, :string
    execute "UPDATE users SET active = 'true' WHERE active_boolean = true"
    execute "UPDATE users SET active = 'inactive' WHERE active_boolean = false"
    remove_column :users, :active_boolean
  end
end

答案 1 :(得分:3)

使用USING clause of ALTER TABLE

,您可以轻松地一次完成
  

可选的USING子句指定如何从旧的计算新列值;如果省略,则默认转换与从旧数据类型转换为new的赋值相同。

简单的SQL类型转换将为您留下字符串'true''false',因此您确实需要添加USING。我绕过AR并手动完成:

connection.execute(%q(
  alter table users
  alter column active
  type text
  using case when active then 'active' else 'inactive' end
))

最重要的部分是using case ...部分。通过欺骗AR做正确的事情,你可以将它与通常的AR-ish change_column一起使用:

class ChangeColumnTypeInUsers < ActiveRecord::Migration
  def up
    change_column :users, :active, "text using case when active then 'active' else 'inactive' end"
  end

  def down
    change_column :users, :active, "boolean using active = 'active'"
  end
end

请注意,我使用text作为列类型。当你说varchar(255)没有限制时,Rails会在数据库中使用:string,这对于PostgreSQL来说是非常没有意义的,因为它处理所有字符串类型pretty much the same internally的存储空间,长度对char(n)varchar(n)的限制实际上使其使用起来比text更昂贵。那么只有时间:string对PostgreSQL有意义,当你有理由包含一个特定的:limit时(然后text列的长度会CHECK约束感觉更有意义,但AR太愚蠢了,无法了解&#34;高级&#34;像CHECK约束这样的事情。

答案 2 :(得分:0)

我还没有这样做,但我认为它只是在您的up方法中添加更多内容或编写单独的迁移来处理up部分。这样......

class ChangeColumnTypeInUsers < ActiveRecord::Migration
  def up
    change_column :users, :active, :string

    User.find_each do |user|
      user.active = "active" if user.active = true
      user.save!
    end 
  end

  def down
    change_column :users, :active, :boolean
  end
end

你也可以把它变成if / else来处理false。应该管用。我只是在我的数据库中的单个用户的控制台中测试它,所以看起来很好。