Использование Rails Migration для другой базы данных, отличной от стандартных «производство» или «разработка» - PullRequest
46 голосов
/ 10 сентября 2009

У меня запущен проект rails, который определяет стандартную продукцию :,: development и: test DB-подключений в config / database.yml

Кроме того, у меня есть определение quiz_development: и quiz_production: определение, указывающее на другой хост / db / user / password

Моя цель теперь состоит в том, чтобы определить миграцию, которая использует "quiz_#{RAILS_ENV}` "в качестве конфигурации базы данных.

Что я пробовал (и не получилось):

  • Настройка ActiveRecord :: Base.connection в файле миграции
  • Изменение задачи db: migrate в rails для установки там ActiveRecord :: Base.connection

Вопрос:

Как заставить rake db: migrate использовать другое определение базы данных?

Спасибо, Frank

Ответы [ 19 ]

35 голосов
/ 11 марта 2012

Ответ гораздо проще. Добавьте это к вашей миграции:

def connection
  ActiveRecord::Base.establish_connection("quiz_#{Rails.env}").connection
end

Это для Rails 3.1. Для Rails 2.X или 3.0 вместо этого используется функция класса (например, def self.connection)

18 голосов
/ 13 июня 2012

Я получил это для работы со следующим кодом.

class AddInProgressToRefHighLevelStatuses < ActiveRecord::Migration
  def connection
    @connection = ActiveRecord::Base.establish_connection("sdmstore_#{Rails.env}").connection
  end

  def change
    add_column :ref_high_level_statuses, :is_in_progress, :boolean, :default => true

    @connection = ActiveRecord::Base.establish_connection("#{Rails.env}").connection
  end
end

Необходимо было установить соединение обратно, чтобы заставить его записать миграцию в таблицу schema_migrations, чтобы rake не пытался повторно запустить миграцию в следующий раз. Это предполагает, что вы хотите, чтобы таблица schema_migrations в конфигурации базы данных по умолчанию отслеживала миграции, отмеченные в управлении версиями для соответствующего проекта.

Мне не удалось запустить миграцию вниз.

13 голосов
/ 15 сентября 2009

Вы должны определить другие базы данных / среды в /config/environments.

После этого вы можете использовать следующую команду для переноса этой конкретной среды.

rake db:migrate RAILS_ENV=customenvironment
11 голосов
/ 27 ноября 2009

Немного поздно, но я имел дело с этой проблемой сегодня, и я придумал эту пользовательскую задачу рейка:

namespace :db do
  desc "Apply db tasks in custom databases, for example  rake db:alter[db:migrate,test-es] applies db:migrate on the database defined as test-es in databases.yml"
  task :alter, [:task,:database] => [:environment] do |t, args|
    require 'activerecord'
    puts "Applying #{args.task} on #{args.database}"
    ActiveRecord::Base.establish_connection(ActiveRecord::Base.configurations[args.database])
    Rake::Task[args.task].invoke
  end
end
8 голосов
/ 15 декабря 2015

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

Я начал пытаться разрешить ее, выполнив ActiveRecord::Base.establish_connection(:history_database), но не смог заставить работать какие-либо варианты без закрытия соединения. Тогда, наконец, я обнаружил решение ниже.

В модели истории после внесения этого изменения:

class History < ActiveRecord::Base

  # Directs queries to a database specifically for History
  establish_connection :history_database

  ...
end

Я смог сделать это в процессе миграции, и он отлично работал:

class CreateHistoriesTableInHistoryDatabase < ActiveRecord::Migration
  def up
    History.connection.create_table :histories do |t|
      ...
    end
  end

  def down
    History.connection.drop_table :histories
  end
end

Это создаст таблицу в другой базе данных, но при этом изменит таблицу schema_migrations в исходной базе данных, чтобы миграция больше не запускалась.

8 голосов
/ 20 октября 2013

Исходя из @Bryan Larsen, если вы используете абстрактный класс для присоединения ряда моделей к другой базе данных и хотите перенести схемы на них, то вы можете сделать это:

class CreatePosts < ActiveRecord::Migration
    def connection
      Post.connection
    end
    def up
      ...
    end
end

с моделью, настроенной примерно так:

class Post < ReferenceData
end

и

class ReferenceData < ActiveRecord::Base
  self.abstract_class = true
  establish_connection "reference_data_#{Rails.env}"
end
7 голосов
/ 24 сентября 2013

Для Rails 3.2 это то, что мы сделали, работает с миграцией вверх и вниз:

class CreateYourTable < ActiveRecord::Migration

  def connection
    @connection ||= ActiveRecord::Base.connection
  end

  def with_proper_connection
    @connection = YourTable.connection
    yield
    @connection = ActiveRecord::Base.connection
  end


  def up
    with_proper_connection do
      create_table :your_table do |t|
      end
    end
  end

  def down
    with_proper_connection do
      drop_table :your_table
    end
  end

end
7 голосов
/ 14 мая 2013

Эй, я копался в этом в течение нескольких дней и закончил с этим решением, просто хотел поделиться им, оно может кому-то помочь.

Вот полный смысл этого. https://gist.github.com/rafaelchiti/5575309 У него есть детали и объяснение. Но найдите ниже более подробную информацию, если они вам нужны.

Подход основан на добавлении пространства имен к уже известным задачам rake. Db: migrate, db: create, db: drop и выполнение этих задач с другой базой данных. И затем при добавлении базового класса активной записи (AR) для соединения на основе конфигурации нового файла database.yml. Таким образом, вам не нужно разбираться с миграциями с помощью соединений, и вы получите чистую структуру каталогов.

Ваша структура будет выглядеть так

config
  |- database.yml
  \- another_database.yml (using the same nomenclature of 'development', 'test', etc).

db
  |- migrate (default migrate directory)
  |- schema.rb
  |- seed.rb

another_db
  |- migrate (migrations for the second db)
  |- schema.rb (schema that will be auto generated for this db)
  |- seed.rb (seed file for the new db)

Затем в своем коде вы можете создать базовый класс и прочитать конфигурацию из этого нового файла database.yml и подключаться к нему только в тех моделях, которые наследуют этот базовый класс AR. (пример в сущности).

Лучший!.

5 голосов
/ 03 февраля 2012
module ActiveRecord::ConnectionSwitch
  def on_connection(options)
    raise ArgumentError, "Got nil object instead of db config options :(" if options.nil?
    ActiveRecord::Base.establish_connection(options)
    yield
  ensure
    ActiveRecord::Base.establish_connection ActiveRecord::Base.configurations[Rails.env]
  end
end

ActiveRecord.send :extend, ActiveRecord::ConnectionSwitch

Если вы поместите это внутрь config/initializers/, вы сможете сделать что-то вроде этого:

ActiveRecord.on_connection ActiveRecord::Base.configurations['production'] do
  Widget.delete_all
end

Это удалит все виджеты из производственной базы данных и после этого убедитесь, что соединение с текущей базой данных Rails env восстановлено.

Если вы просто хотите сделать его доступным в своих миграциях, расширьте класс ActiveRecord::Migration.

4 голосов
/ 13 февраля 2015

В rails 3.2 добавление метода подключения к вашей миграции НЕ работает. Так что все ответы вроде

def connection
 @connection ||= ActiveRecord::Base.establish_connection
end

просто не будет работать (не может down, не работает с change, потеря соединения и т. Д.). Причина этого в том, что классы ActiveRecord :: Migration и Migrator имеют жестко запрограммированные соединения в ActiveRecord :: Base all over место .

К счастью этот пост указал мне на этот билет , который имеет хорошее решение, а именно, переопределение фактической задачи рейка .

В итоге я использовал немного другую задачу rake, чтобы я мог быть более конкретным в отношении миграций, которые я запускаю на другой базе данных (мы пытались поддерживать несколько версий БД):

Вот моя библиотека lib / task / database.rake

# Augment the main migration to migrate your engine, too.
task 'db:migrate', 'nine_four:db:migrate'

namespace :nine_four do
    namespace :db do
        desc 'Migrates the 9.4 database'
        task :migrate => :environment do
            with_engine_connection do
                ActiveRecord::Migrator.migrate("#{File.dirname(__FILE__)}/../../nine_four/migrate", ENV['VERSION'].try(:to_i))
            end
        end
    end
end

# Hack to temporarily connect AR::Base to your engine.
def with_engine_connection
    original = ActiveRecord::Base.remove_connection
    ActiveRecord::Base.establish_connection("#{ Rails.env }_nine_four")
    yield
ensure
    ActiveRecord::Base.establish_connection(original)
end

Это позволяет нам поместить миграции, специфичные для одной базы данных, в их собственный подкаталог (nine_four / migrations вместо db / migrations). Это также дает каждой базе данных полную изоляцию с точки зрения их схемы и версий миграции. Единственный недостаток - запуск двух задач rake (db: migrate и nine_four: db: migrate).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...