如何在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