Ruby-on-rails 在与标准“生产”或“开发”不同的数据库上使用 Rails Migration

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1404620/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 21:44:17  来源:igfitidea点击:

Using Rails Migration on different database than standard "production" or "development"

ruby-on-railsrubydatabasemigration

提问by thenoseman

I have a rails project running that defines the standard production:, :development and :test DB-connections in config/database.yml

我有一个 Rails 项目正在运行,它定义了 config/database.yml 中的标准 production:、:development 和 :test DB-connections

In addition I have a quiz_development: and quiz_production: definition pointing to a differnet host/db/user/password

此外,我有一个 quiz_development: 和 quiz_production: 定义指向一个不同的主机/数据库/用户/密码

My goal now is to define a Migration that uses "quiz_#{RAILS_ENV}`" as its database configuration.

我现在的目标是定义一个使用“ quiz_#{RAILS_ENV}`”作为其数据库配置的迁移。

What I have tried (and failed):

我尝试过(但失败了):

  • Setting ActiveRecord::Base.connection in the Migration file
  • Changing the db:migrate task in rails to set ActiveRecord::Base.connection there
  • 在迁移文件中设置 ActiveRecord::Base.connection
  • 在 rails 中更改 db:migrate 任务以在那里设置 ActiveRecord::Base.connection

Question:

题:

How can I make rake db:migrate use that other database definition?

如何让 rake db:migrate 使用其他数据库定义?

Thanks, Frank

谢谢,弗兰克

采纳答案by Siu

A bit late, but I was dealing with this problem today and I came up with this custom rake task:

有点晚了,但我今天正在处理这个问题,我想出了这个自定义 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

回答by Bryan Larsen

There's a much easier answer. Add this to your migration:

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

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

That's for Rails 3.1. For Rails 2.X or 3.0 it's a class function instead (eg def self.connection)

这适用于 Rails 3.1。对于 Rails 2.X 或 3.0,它是一个类函数(例如def self.connection

回答by Marlin Pierce

I got this to work with the following code.

我让它与以下代码一起工作。

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

It was necessary to set the connection back to get it to write the migration to the schema_migrations table so rake would not try to re-run the migration the next time. This assumes that you want the schema_migrations table in the default database configuration to keep track of the migrations checked into version control for the corresponding project.

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

I was unable to get the down migration to work.

我无法使向下迁移工作。

回答by Bitterzoet

You should define the other databases/environments in /config/environments.

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

After that you can use the following command to migrate that specific environment.

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

rake db:migrate RAILS_ENV=customenvironment

回答by MTarantini

I recently struggled with the same problem. The goal was to split off a histories table to a different database since it was already so large and still growing very quickly.

我最近也遇到了同样的问题。目标是将历史记录表拆分到不同的数据库中,因为它已经很大并且仍在快速增长。

I started trying to resolve it by doing ActiveRecord::Base.establish_connection(:history_database), but could not get any variations of that way to work without the connection being closed. Then finally I discovered the solution below.

我开始尝试通过执行 来解决它ActiveRecord::Base.establish_connection(:history_database),但是如果没有关闭连接,就无法以这种方式工作。然后最后我发现了下面的解决方案。

In the History model after making this change:

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

class History < ActiveRecord::Base

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

  ...
end

I was able to do this in the migration and it worked perfectly:

我能够在迁移中做到这一点,而且效果很好:

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

  def down
    History.connection.drop_table :histories
  end
end

This will create the table in a different database, yet modify the schema_migrations table in the original database so the migration does not run again.

这将在不同的数据库中创建表,但修改原始数据库中的 schema_migrations 表,因此迁移不会再次运行。

回答by bouchard

Following on from @Bryan Larsen, if you're using an abstract Class to attach a series of models to a different database, and would like to migrate schemas on them, then you can do this:

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

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

with a model set up something like:

模型设置如下:

class Post < ReferenceData
end

and

class ReferenceData < ActiveRecord::Base
  self.abstract_class = true
  establish_connection "reference_data_#{Rails.env}"
end

回答by Rafael

Hey I been digging into this for a few days and I ended up with this solution, just wanted to share it, it might help someone.

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

Here the complete gist for it. https://gist.github.com/rafaelchiti/5575309It has details ans explanation. But find below more details if you need them.

这是它的完整要点。https://gist.github.com/rafaelchiti/5575309有详细说明。但如果您需要,请在下面找到更多详细信息。

The approach is based on adding a namespace to the already known rake tasks db:migrate, db:create, db:drop and perform those tasks with a different database. And then in adding a base active record (AR) class for connecting based on the configuration of the new database.yml file. This way you don't need to hack around the migrations with connection stuff and you get a clean directory structure.

该方法基于向已知的 rake 任务 db:migrate、db:create、db:drop 添加命名空间,并使用不同的数据库执行这些任务。然后根据新的database.yml 文件的配置添加一个用于连接的基本活动记录(AR)类。这样你就不需要用连接的东西来绕过迁移,你会得到一个干净的目录结构。

Your structure will end up like this

你的结构最终会是这样

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)

Then in your code you can create a base class and read the config from this new database.yml file and connect to it only on the models that inherit from that AR base class. (example in the gist).

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

Best!.

最好的事物!。

回答by zephyr

For Rails 3.2, this is what we did, works with migrating up and down:

对于 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

回答by TheDeadSerious

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

If you place this inside config/initializers/you'll be able to do something like this:

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

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

This will delete all widgets on the production db and make sure the connection to the current Rails env's db is re-established after that.

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

If you just want to make it available in your migrations insead extend the ActiveRecord::Migrationclass.

如果您只是想让它在您的迁移中可用 insead 扩展ActiveRecord::Migration类。

回答by Ryan

In rails 3.2, adding a connection method to your migration does NOT work. So all of the answers like

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

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

simply won't work (can't down, doesn't work with change, connection lost, etc.) The reason for this is that the ActiveRecord::Migration and Migrator class have connections hard-coded to ActiveRecord::Base allovertheplace.

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

Fortunately this postpointed me to this ticketwhich has a good solution, namely overriding the actual rake task.

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

I ended up using a slightly different rake task so that I could be specific about the migrations I run on the different database (we were trying to support multiple db versions):

我最终使用了一个稍微不同的 rake 任务,以便我可以具体说明我在不同数据库上运行的迁移(我们试图支持多个数据库版本):

Here's my lib/task/database.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

This allows us to put migrations specific to one database in their own subdirectory (nine_four/migrations instead of db/migrations). It also gives each database total isolation in terms of their schema and migration versions.The only downside is having two rake tasks to run (db:migrate and nine_four:db:migrate).

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