Как написать условные миграции в рельсах?
Я ищу способы записи миграций в рельсах, которые могут выполняться в базе данных много раз без сбоев.
Например, скажем, у меня есть эта миграция:
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 был изменен неожиданно, например), моя миграция не будет подтверждать, что это дубликат!
Итак, как выполнить эту миграцию, только если это необходимо?
Спасибо
Ответы
Ответ 1
Вы можете сделать что-то вроде этого:
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
Это будет reset информация о столбцах до ее начала - убедитесь, что модель профиля имеет самую последнюю информацию столбца из фактической таблицы. Затем он добавит столбец, если он не существует. То же самое происходит для функции down, но она удаляет только столбец, если он существует.
Если у вас несколько вариантов использования, вы можете включить код в функцию и повторно использовать это в своих миграциях.
Ответ 2
Для Rails 3.X существует метод column_exists?(:table_name, :column_name)
.
Для Rails 2.X вы можете проверить наличие столбцов со следующим:
columns("<table name>").index {|col| col.name == "<column name>"}
... или если вы не находитесь в файле миграции:
ActiveRecord::Base.connection.columns("<table name>").index {|col| col.name == "<column name>"}
Если он возвращает nil, такой столбец не существует. Если он возвращает Fixnum, то столбец существует. Естественно, вы можете задать более селективные параметры между {...}
, если вы хотите идентифицировать столбец больше, чем просто его имя, например:
{ |col| col.name == "foo" and col.sql_type == "tinyint(1)" and col.primary == nil }
Ответ 3
Это должно работать
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
Ответ 4
Привязка моей миграции в условном режиме работала для меня.
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