通过Rails迁移添加数据库列,并根据另一列填充它 [英] add a database column with Rails migration and populate it based on another column

查看:70
本文介绍了通过Rails迁移添加数据库列,并根据另一列填充它的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在编写一个迁移,以向表中添加一列.该列的值取决于另外两个现有列的值.最好/最快的方法是什么? 目前,我有此方法,但由于组表可能很大,因此不确定这是否是最佳方法.

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

解决方案

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

例如,如果将来更改is_activeis_live属性的行为,则此迁移可能会中断.较旧的迁移将首先针对新的模型代码运行,并且可能会失败.在您的基本示例中,它可能不会出现,但是在添加字段并且无法运行验证之前,这使我在部署中非常恼火(我知道您的代码正在跳过验证,但总的来说,这是个问题).

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

如果您有一些繁琐的业务逻辑,或者只是想让代码看起来更像Railsy,另一种选择是,在迁移中包含模型的基本版本(在编写迁移时就存在)文件本身.例如,您可以将此类放在迁移文件中:

class Group < ActiveRecord::Base
end

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

您甚至可以考虑将整个方法从模型复制到迁移版本中.如果这样做,请记住,也不要从那里引用应用程序中的任何外部模型或库,否则将来它们可能会发生变化.这包括gems甚至可能包括一些核心的Ruby/Rails类,因为gems中打破API的更改非常普遍(我正在向您介绍Rails 3.0、3.1和3.2!).

I'm writing a migration to add a column to a table. The value of the column is dependent on the value of two more existing columns. What is the best/fastest way to do this? Currently I have this but not sure if it's the best way since the groups table is can be very large.

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

解决方案

It's generally a bad idea to reference your models from your migrations like this. The problem is that the migrations run in order and change the database state as they go, but your models are not versioned at all. There's no guarantee that the model as it existed when the migration was written will still be compatible with the migration code in the future.

For example, if you change the behavior of the is_active or is_live attributes in the future, then this migration might break. This older migration is going to run first, against the new model code, and may fail. In your basic example here, it might not crop up, but this has burned me in deployment before when fields were added and validations couldn't run (I know your code is skipping validations, but in general this is a concern).

My favorite solution to this is to do all migrations of this sort using plain SQL. It looks like you've already considered that, so I'm going to assume you already know what to do there.

Another option, if you have some hairy business logic or just want the code to look more Railsy, is to include a basic version of the model as it exists when the migration is written in the migration file itself. For example, you could put this class in the migration file:

class Group < ActiveRecord::Base
end

In your case, that alone is probably sufficient to guarantee that the model will not break. Assuming active and live are boolean fields in the table at this time (and thus would be whenever this migration was run in the future), you won't need any more code at all. If you had more complex business logic, you could include it in this migration-specific version of model.

You might even consider copying whole methods from your model into the migration version. If you do that, bear in mind that you shouldn't reference any external models or libraries in your app from there, either, if there's any chance that they will change in the future. This includes gems and even possibly some core Ruby/Rails classes, because API-breaking changes in gems are very common (I'm looking at you, Rails 3.0, 3.1, and 3.2!).

这篇关于通过Rails迁移添加数据库列,并根据另一列填充它的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆