使用Rails迁移添加数据库列,并根据另一列填充它

时间:2013-03-08 00:20:37

标签: ruby-on-rails activerecord migration rails-activerecord rails-migrations

我正在编写迁移以向表中添加列。列的值取决于另外两个现有列的值。最好/最快的方法是什么? 目前我有这个,但不确定它是否是最好的方式,因为组表可能非常大。

class AddColorToGroup < ActiveRecord::Migration
  def self.up
    add_column :groups, :color, :string
    Groups = Group.all.each do |g|
      c = "red" if g.is_active && is_live 
      c = "green" if g.is_active
      c = "orange"
      g.update_attribute(:type, c)
    end
  end

  def self.down

  end
end

4 个答案:

答案 0 :(得分:15)

从这样的迁移中引用模型通常是一个坏主意。问题是迁移按顺序运行并随着时间的推移更改数据库状态,但您的模型根本没有版本化。无法保证编写迁移时存在的模型在将来仍然与迁移代码兼容。

例如,如果您将来更改is_activeis_live属性的行为,则此迁移可能会中断。对于新的模型代码,此旧迁移将首先运行,并且可能会失败。在这里的基本示例中,它可能不会出现,但是在添加字段并且验证无法运行之前,这已经在部署中烧毁了我(我知道您的代码正在跳过验证,但通常这是一个问题)。

我最喜欢的解决方案是使用纯SQL进行此类迁移。看起来你已经考虑过了,所以我假设你已经知道该怎么做了。

另一个选择,如果你有一些毛茸茸的业务逻辑或只是希望代码看起来更像Railsy,那就是在迁移中编写迁移时的基本版本文件本身。例如,您可以将此类放在迁移文件中:

class Group < ActiveRecord::Base
end

在您的情况下,仅此一项可能足以保证模型不会破裂。假设此时activelive是表中的布尔字段(因此将来会运行此迁移),您根本不需要任何其他代码。如果您有更复杂的业务逻辑,则可以将其包含在此特定于迁移的模型版本中。

您甚至可以考虑将模型中的整个方法复制到迁移版本中。如果你这样做,请记住,如果你的应用程序有可能在将来发生变化,你也不应该从那里引用任何外部模型或库。这包括gems甚至可能是一些核心Ruby / Rails类,因为gems中的API破坏性变化非常普遍(我正在看着你,Rails 3.0,3.1和3.2!)。

答案 1 :(得分:2)

我强烈建议改为完成三个查询。始终利用数据库与循环数组中的一堆项目。我认为这样的事情可行。

为了写这个,我假设is_active检查一个活动的字段,其中1是活动的。我假设现场也一样。

Rails 3方法

class AddColorToGroup < ActiveRecord::Migration
  def self.up
    add_column :groups, :color, :string
    Group.where(active: 1, live: 1).update_all(type: "red")
    Group.where(active: 1, live: 0).update_all(type: "green")
    Group.where(active: 0, live: 0).update_all(type: "orange")
   end
 end

欢迎查看update_all here的文档。

Rails 2.x方法

class AddColorToGroup < ActiveRecord::Migration
  def self.up
    add_column :groups, :color, :string
    Group.update_all("type = red", "active = 1 AND live = 1")
    Group.update_all("type = red", "active = 1 AND live = 0")
    Group.update_all("type = red", "active = 0 AND live = 0")
   end
 end

Rails 2 documentation

答案 2 :(得分:1)

我会在

中这样做
after_create
# or
after_save
在ActiveRecord模型中

class Group < ActiveRecord::Base
  attr_accessor :color

  after_create :add_color

  private

  def add_color
    self.color = #the color (wherever you get it from)
  end

end

或者在迁移过程中你可能需要像这样做一些SQL:

execute('update groups set color = <another column>')

以下是Rails指南中的一个示例:

http://guides.rubyonrails.org/migrations.html#using-the-up-down-methods

答案 3 :(得分:0)

在类似情况下,我最终使用add_column添加列,然后使用直接SQL更新列的值。我使用直接SQL而不是每Jim Stewart's answer的模型,因为它不依赖于模型的当前状态与基于正在运行的迁移的表的当前状态。

class AddColorToGroup < ActiveRecord::Migration
  def up
    add_column :groups, :color, :string
    execute "update groups set color = case when is_active and is_live then 'red' when is_active then 'green' else 'orange' end"
  end

  def down
    remove_column :groups, :color
  end
end