Подтвердить что ты не робот

Как написать условные миграции в рельсах?

Я ищу способы записи миграций в рельсах, которые могут выполняться в базе данных много раз без сбоев.

Например, скажем, у меня есть эта миграция:

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 был изменен неожиданно, например), моя миграция не будет подтверждать, что это дубликат!

Итак, как выполнить эту миграцию, только если это необходимо?

Спасибо

4b9b3361

Ответ 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