如何在rails中编写条件迁移?

我正在寻找在rails中编写迁移的方法,可以多次对数据库执行而不会失败。

比如说我有这个迁移:

class AddUrlToProfile < ActiveRecord::Migration def self.up add_column :profile, :url, :string end def self.down remove_column :profile, :url end end 

如果url列已存在于Profile表中(例如,如果schema.rb已被意外修改),我的迁移将失败,说它是重复的!

那么如果必须执行此迁移呢?

谢谢

你可以这样做:

 class AddUrlToProfile < ActiveRecord::Migration def self.up Profile.reset_column_information add_column(:profile, :url, :string) unless Profile.column_names.include?('url') end def self.down Profile.reset_column_information remove_column(:profile, :url) if Profile.column_names.include?('url') end end 

这将在列信息开始之前重置列信息 - 确保配置文件模型具有实际表中的最新列信息。 然后它只会添加列,如果它不存在。 down函数也会发生同样的事情,但它只会删除列(如果存在)。

如果您有多个用例,则可以将代码分解为函数并在迁移中重复使用。

对于Rails 3.X ,有column_exists?(:table_name, :column_name)方法。

对于Rails 2.X ,您可以使用以下内容检查列的存在:

 columns("").index {|col| col.name == ""}

…或者如果您不在迁移文件中:

 ActiveRecord::Base.connection.columns("").index {|col| col.name == ""}

如果返回nil,则不存在此列。 如果它返回Fixnum,则该列确实存在。 当然,如果要通过多个名称来标识列,可以在{...}之间添加更多选择性参数,例如:

 { |col| col.name == "foo" and col.sql_type == "tinyint(1)" and col.primary == nil } 

这应该工作

 def self.table_exists?(name) ActiveRecord::Base.connection.tables.include?(name) end if table_exists?(:profile) && !Profile.column_names.include?("url") add_column :profile, :url, :string end 

在条件下包装我的迁移对我有用。 Rails 4.X

 class AddUrlToProfile < ActiveRecord::Migration unless Profile.column_names.include?("url") def self.up add_column :profile, :url, :string end def self.down remove_column :profile, :url end end end