web-dev-qa-db-ja.com

Railsを使用して、標準の「本番」または「開発」とは異なるデータベースでの移行

私はRailsプロジェクトを実行していて、標準のproduction:、:developmentおよび:test DB-connectionsをconfig/database.ymlで定義しています。

さらに、quiz_development:とquiz_production:の定義があり、別のホスト/ db/user/passwordをポイントしています

私の目標は、データベース構成として「quiz_#{Rails_ENV} `」を使用する移行を定義することです。

私が試した(そして失敗した)もの:

  • 移行ファイルでActiveRecord :: Base.connectionを設定する
  • Railsでdb:migrateタスクを変更してActiveRecord :: Base.connectionをそこに設定する

質問:

Rake db:migrateで他のデータベース定義を使用するにはどうすればよいですか?

ありがとう、フランク

46
thenoseman

少し遅れましたが、今日この問題に対処していて、次のカスタムレーキタスクを思いつきました。

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
11
Siu

はるかに簡単な答えがあります。これを移行に追加します。

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

Rails 3.1の場合です。Rails 2.Xまたは3.0の場合は、代わりにクラス関数です(例:def self.connection

36
Bryan Larsen

私はこれを次のコードで動作させるようにしました。

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テーブルで、対応するプロジェクトのバージョン管理にチェックインされた移行を追跡することを前提としています。

ダウンマイグレーションを機能させることができませんでした。

18
Marlin Pierce

/ config/environmentsで他のデータベース/環境を定義する必要があります。

その後、次のコマンドを使用して、その特定の環境を移行できます。

rake db:migrate Rails_ENV=customenvironment
13
Bitterzoet

私は最近同じ問題で苦労しました。履歴テーブルはすでに非常に大きく、しかも非常に急速に成長しているため、履歴テーブルを別のデータベースに分割することが目標でした。

私は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テーブルが変更されます。

10
MTarantini

@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
8
bouchard

ちょっと私はこれを数日間掘り下げていました、そして私はこの解決策に終わりました、それを共有したいだけでした、それは誰かを助けるかもしれません。

ここでそれのための完全な要点。 https://Gist.github.com/rafaelchiti/5575309 詳細と説明があります。ただし、必要に応じて、以下の詳細をご覧ください。

このアプローチは、既知のrakeタスクdb:migrate、db:create、db:dropに名前空間を追加し、それらのタスクを別のデータベースで実行することに基づいています。次に、新しいdatabase.ymlファイルの構成に基づいて接続するためのベースアクティブレコード(AR)クラスを追加します。この方法では、接続に関するものでマイグレーションをハッキングする必要がなく、クリーンなディレクトリ構造が得られます。

あなたの構造はこのようになります

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基本クラスから継承するモデルでのみそれに接続できます。 (要旨の例)。

ベスト!。

7
Rafael

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
zephyr
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クラスを拡張します。

5
TheDeadSerious

Rails 3.2では、接続方法を移行に追加しても機能しません。そのため、次のようなすべての答えは

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

単に機能しない(downできない、changeと機能しない、接続が失われるなど)。これの理由は、ActiveRecord :: MigrationおよびMigratorクラスに接続があるためです。 ActiveRecord :: Base allovertheplace にハードコードされています。

幸いなことに この投稿このチケット を指摘しました。これは良い解決策、つまり実際の レーキタスク をオーバーライドするものです。

異なるデータベースで実行する移行について特定できるように、最終的にはわずかに異なるrakeタスクを使用することになりました(複数のdbバージョンをサポートしようとしました)。

これが私の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

これにより、1つのデータベースに固有のマイグレーションを独自のサブディレクトリに配置できます(db/migrationsの代わりにnine_four/migrations)。 また、スキーマと移行バージョンに関して各データベースを完全に分離します。唯一の欠点は、実行する2つのRakeタスク(db:migrateとnine_four :db:migrate)。

4
Ryan

別の環境で移行を実行することに加えて、スキーマを個別のファイルに格納することも必要です。コマンドラインからこれを行うことができます:

Rails_ENV=quiz_development SCHEMA=db/schema_quiz_development.rb rake db:migrate

しかし、私はカスタムレーキタスクアプローチが好きなので、代わりにこれを入力できます。

rake db:with[quiz_development, db:migrate]

これがレーキタスクです。

namespace :db do
  desc "Run :task against :database"
  task :with, [:database,:task] => [:environment] do |t, args|
    puts "Applying #{args.task} to #{args.database}"
    ENV['SCHEMA'] ||= "#{Rails.root}/db/schema_#{args.database}.rb"
    begin
      oldRailsEnv = Rails.env
      Rails.env = args.database
      ActiveRecord::Base.establish_connection(args.database)
      Rake::Task[args.task].invoke
    ensure
      Rails.env = oldRailsEnv
    end
  end
end
2
Eric Dobbs

私はこれを行うための素晴らしいクリーンな方法を見つけました:

class CreateScores < ActiveRecord::Migration

  class ScoresDB < ActiveRecord::Base
    establish_connection("scores_#{Rails.env}")
  end

  def connection
    ScoresDB.connection
  end

  def up
    create_table :scores do |t|
      t.text :account_id
      t.text :offer
    end
  end

  def down
    drop_table :scores
  end
end
2
just.jules

rake db:rollbackもサポートするこのバージョンを使用できます。

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

  def reset_connection
    ActiveRecord::Base.establish_connection(Rails.env)
  end

  def up
    # make changes

    reset_connection
  end

  def self.down
    # reverse changes

    reset_connection
  end
end
1
nlsrchtr
class Article < ActiveRecord::Base

    ActiveRecord::Base.establish_connection(
      :adapter  => "mysql2",
      :Host     => "localhost",
      :username => "root",
      :database => "test"
    )
end

そして:

class Artic < Aritcle
    self.table_name = 'test'

    def self.get_test_name()
        query = "select name from testing"
        tst = connection.select_all(query) #select_all is important!
        tst[0].fetch('name')
    end
end

実行するには、Artic.get_test_nameを呼び出すことができます。

@TheDeadSeriousの回答に基づく:

module ActiveRecord::ConnectionSwitch  
  def on_connection(connection_spec_name)
    raise ArgumentError, "No connection specification name specified. It should be a valid spec from database.yml" unless connection_spec_name
    ActiveRecord::Base.establish_connection(connection_spec_name)
    yield
  ensure
    ActiveRecord::Base.establish_connection(Rails.env)
  end
end

ActiveRecord.send :extend, ActiveRecord::ConnectionSwitch

使用法:

ActiveRecord.on_connection "sdmstore_#{Rails.env}" do
  Widget.delete_all
end
0
Pratik Khadloya

Rails_ENVとしてquiz_developmentを使用してみましたか("quiz_#{Rails_ENV}")?

Rails_ENV=quiz_development rake db:migrate
0
hgmnz

異なるデータベースに個別のコネクタクラスを作成し、それらを移行で使用することで、これを機能させました。

class AddExampleToTest < ActiveRecord::Migration
  def connection
    @connection = OtherDatabaseConnector.establish_connection("sdmstore_#{Rails.env}").connection
  end
  def up
    add_column :test, :example, :boolean, :default => true

    @connection = MainDatabaseConnector.establish_connection("#{Rails.env}").connection
  end
  def down
    remove_column :test, :example

    @connection = MainDatabaseConnector.establish_connection("#{Rails.env}").connection
  end
end

これらのコネクタクラスを初期化子で定義できます。

class MainDatabaseConnector < ActiveRecord::Base
end
class OtherDatabaseConnector < ActiveRecord::Base
end

ActiveRecord :: Baseは、クラスによってインデックスが付けられたハッシュである接続プールを保持します。 詳細はこちら 。したがって、個別の接続に個別のクラスを使用することで、クローズされた接続エラーから保護されます。

また、upの代わりにdownchangeを使用すると、問題なく移行をロールバックできます。この理由はまだわかりません。

0
gauravm31

また、すべてのquiz_関連の移行をdb /ディレクトリの別のサブフォルダーに移動し、通常の移行機能をミラーリングしているが、そのサブディレクトリで移行を探すrakeタスクを追加することもできます。超エレガントではないかもしれませんが、うまくいきます。すでにRailsにあるrakeタスクをコピーして貼り付け、少し変更するだけです。

0
ealdent

wordpress投稿をRails Webサイトに表示し、マルチマジック接続ジェムを使用したくない場合。以下のコードを使用できます。 wordpressブログからデータを取得するために。

 class Article < ActiveRecord::Base

    ActiveRecord::Base.establish_connection(
     :adapter  => "mysql2",
     :Host     => "localhost",
     :username => "root",
     :database => "blog"
    )

    self.table_name = 'wp_posts'

    def self.get_post_data()
        query = "select name from testing"
        tst = connection.select_all(query)
        tst[0].fetch('name')
    end
end