在标准"生产"或"开发"之外的不同数据库上使用Rails迁移

the*_*man 48 ruby database migration ruby-on-rails

我有一个运行的rails项目,它定义了标准生产:,:开发和:在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使用其他数据库定义?

谢谢,弗兰克

Bry*_*sen 36

有一个更简单的答案.将此添加到您的迁移中:

def connection
  ActiveRecord::Base.establish_connection("quiz_#{Rails.env}").connection
end
Run Code Online (Sandbox Code Playgroud)

那是Rails 3.1.对于Rails 2.X或3.0,它是一个类函数(例如def self.connection)

  • 这似乎接近真正的答案,但失败并出现错误:关系"schema_migrations"不存在. (2认同)

Mar*_*rce 18

我使用以下代码使用它.

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
Run Code Online (Sandbox Code Playgroud)

有必要重新设置连接以使其将迁移写入schema_migrations表,因此rake不会尝试在下次重新运行迁移.这假定您希望默认数据库配置中的schema_migrations表跟踪已检入相应项目的版本控制的迁移.

我无法让下行迁移工作.


Bit*_*oet 14

您应该在/ config/environments中定义其他数据库/环境.

之后,您可以使用以下命令迁移该特定环境.

rake db:migrate RAILS_ENV=customenvironment
Run Code Online (Sandbox Code Playgroud)


Siu*_*Siu 11

有点晚了,但我今天正在处理这个问题,我想出了这个自定义rake任务:

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
Run Code Online (Sandbox Code Playgroud)


MTa*_*ini 10

我最近一直在努力解决同样的问题.目标是将历史表分割到不同的数据库,因为它已经很大并且仍在快速增长.

我开始尝试通过这样做来解决它ActiveRecord::Base.establish_connection(:history_database),但是如果没有关闭连接,就无法获得这种方式的任何变化.最后我发现了下面的解决方案.

在进行此更改后的历史模型中:

class History < ActiveRecord::Base

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

  ...
end
Run Code Online (Sandbox Code Playgroud)

我能够在迁移中做到这一点并且它完美地工作:

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

  def down
    History.connection.drop_table :histories
  end
end
Run Code Online (Sandbox Code Playgroud)

这将在不同的数据库中创建表,但修改原始数据库中的schema_migrations表,以便不再运行迁移.


小智 8

嘿,我已经深入研究了几天,我最终得到了这个解决方案,只想分享它,它可能对某人有所帮助.

这里有完整的要点.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)
Run Code Online (Sandbox Code Playgroud)

然后在您的代码中,您可以创建一个基类并从这个新的database.yml文件中读取配置,并仅在从该AR基类继承的模型上连接它.(在要点中的例子).

最好!.

  • Gist不再可用,取决于外部资源降低了答案的质量,特别是随着时间消失 (3认同)

bou*_*ard 8

继@Bryan Larsen之后,如果您使用抽象类将一系列模型附加到不同的数据库,并希望在它们上迁移模式,那么您可以这样做:

class CreatePosts < ActiveRecord::Migration
    def connection
      Post.connection
    end
    def up
      ...
    end
end
Run Code Online (Sandbox Code Playgroud)

用模型设置如下:

class Post < ReferenceData
end
Run Code Online (Sandbox Code Playgroud)

class ReferenceData < ActiveRecord::Base
  self.abstract_class = true
  establish_connection "reference_data_#{Rails.env}"
end
Run Code Online (Sandbox Code Playgroud)


小智 7

对于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
Run Code Online (Sandbox Code Playgroud)


The*_*ous 5

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
Run Code Online (Sandbox Code Playgroud)

如果你把它放在里面,config/initializers/你将能够做这样的事情:

ActiveRecord.on_connection ActiveRecord::Base.configurations['production'] do
  Widget.delete_all
end
Run Code Online (Sandbox Code Playgroud)

这将删除生产数据库上的所有小部件,并确保在此之后重新建立与当前Rails环境的db的连接.

如果您只想在迁移中使其可用,则会扩展ActiveRecord::Migration该类.


小智 5

在 rails 3.2 中,向迁移添加连接方法不起作用。所以所有的答案都像

def connection
 @connection ||= ActiveRecord::Base.establish_connection
end
Run Code Online (Sandbox Code Playgroud)

是行不通的(不能down,不与不工作change,连接丢失等),这样做的原因是,ActiveRecord的::移民和迁移类具有连接硬编码的ActiveRecord :: Base的所有 地方.

幸运的是,这篇文章向我指出了这张票,它有一个很好的解决方案,即覆盖实际的rake 任务

我最终使用了一个稍微不同的 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
Run Code Online (Sandbox Code Playgroud)

这允许我们将特定于一个数据库的迁移放在它们自己的子目录中(nine_four/migrations 而不是 db/migrations)。它还使每个数据库在其架构和迁移版本方面完全隔离。唯一的缺点是有两个 rake 任务要运行(db:migrate 和 Nine_four:db:migrate)。