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
Using Rails Migration on different database than standard "production" or "development"
提问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)。

