我正在尝试在我的Rails应用程序中转换一个列,但为了论证,让我假装我试图将age
表中的users
列更改为字符串表示而不是int
在我的迁移中,我有这个;
def.self up
add_column :users, :age_text, :string
users = User.find(:all)
users.each do |u|
u.age_text = convert_to_text(u.age)
u.save
end
end
def self.convert_to_text(number)
#code here to convert 1 to 'one' etc
end
但它似乎没有起作用,我在这里尝试迁移甚至可能吗?
答案 0 :(得分:58)
你想做的事情是可能的,我会说正确的事情。
但是,您需要重新加载要在迁移中更新的模型类的列信息,以便Rails了解新列。试试这个:
def.self up
add_column :users, :age_text, :string
User.reset_column_information
users = User.find(:all)
users.each do |u|
u.age_text = convert_to_text(u.age)
u.save
end
end
另请注意,如果您的桌子很大,逐个更新会花费很长时间。请注意这一点。
答案 1 :(得分:39)
由于我是新来的,所以我不能对上述内容发表评论,所以我会添加自己的答案。
通常在迁移中操纵数据是一个不错的想法。如果模型逻辑发生变化,直接模型访问的迁移可能会卡住。
想象一下,在第二次迁移中,您添加了一个新列。您希望使用新数据为该列提供种子。
我们还要说几周后你会为模型添加一个新的验证 - 一个在第二次迁移中尚不存在的字段上运行的验证。如果您曾经从迁移0构建数据库,那么您会遇到一些问题。
我强烈建议使用迁移来更改列和其他方法来管理数据库数据,尤其是在迁移到生产时。
答案 2 :(得分:4)
以下是我运行转换数据的示例迁移。您可以轻松地将其转换为使用整数而不是字符串。在SQL中进行转换比在Rails中加载每一行要快得多。
class ConvertCommentTextToText < ActiveRecord::Migration
def up
add_column :comments, :text_tmp, :text
# copy text column
execute <<-SQL
update comments set text_tmp = text
SQL
remove_column :comments, :text
rename_column :comments, :text_tmp, :text
end
def down
add_column :comments, :text_tmp, :string
# copy text column
execute <<-SQL
update comments set text_tmp = text
SQL
remove_column :comments, :text
rename_column :comments, :text_tmp, :text
end
end
并测试它:
rake db:migrate
rake db:rollback
rake db:migrate
答案 3 :(得分:0)
我想说,如果您在回滚迁移版本时可以“撤消”导入的数据,那么将导入放入迁移是合适的。
例如,我有一个迁移,它设置了很多查找表和其他元数据。在此阶段填充这些表的数据。随着这些查找表的数据发生变化,我创建了新的YAML文件,用于存储元数据并在后续迁移中加载这些文件(并取消那些YAMLS,在退出迁移版本时重新加载以前的YAML文件)。这很干净。我有这些文件的文件(在我的情况下在不同的明确定义的文件夹中):
002_setup_meta_data.rb
002_meta_data.yaml
007_change_meta_data.rb
007_meta_data.yaml
如果您将“生产”数据从另一个系统导入到事务(非静态)表中,那么我会说使用迁移是不合适的。然后我会遵循Brian Hogan关于使用rake任务的建议。