我有一个运行的rails项目,它定义了标准生产:,:开发和:在config / database.yml中测试数据库连接
另外我有一个quiz_development:和quiz_production:定义指向不同的主机/ db / user / password
我现在的目标是定义一个使用“quiz_#{RAILS_ENV
}”作为其数据库配置的迁移。
我尝试过(并且失败了):
题:
如何使rake db:migrate使用其他数据库定义?
谢谢,弗兰克
有点晚了,但我今天正在处理这个问题,我想出了这个自定义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
在rails 3.2中,为迁移添加连接方法不起作用。所以所有答案都是如此
def connection
@connection ||= ActiveRecord::Base.establish_connection
end
根本不起作用(不能down
,不能与change
,连接丢失等)。原因是ActiveRecord :: Migration和Migrator类的连接硬编码为ActiveRecord :: Base all over the place。
幸运的是,this post向我指出this ticket有一个很好的解决方案,即压倒实际的rake task。
我最终使用了一个略有不同的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
这允许我们将特定于一个数据库的迁移放在它们自己的子目录中(nine_four / migrations而不是db / migrations)。它还为每个数据库提供了模式和迁移版本的完全隔离。唯一的缺点是要运行两个rake任务(db:migrate和nine_four:db:migrate)。
除了在不同的环境中运行迁移之外,我还希望将模式放在单独的文件中。您可以从命令行执行此操作:
RAILS_ENV=quiz_development SCHEMA=db/schema_quiz_development.rb rake db:migrate
但我喜欢自定义rake任务方法,所以我可以输入:
rake db:with[quiz_development, db:migrate]
这是rake任务:
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
我发现了一个非常干净的方法:
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
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来执行。
你可以使用这个版本,它也支持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
您是否尝试过将quiz_development用作RAILS_ENV(而不是试图让它使用"quiz_#{RAILS_ENV}"
)?
RAILS_ENV=quiz_development rake db:migrate
您还可以将所有与quiz_相关的迁移移动到db /目录中的单独子文件夹中,然后添加镜像常规迁移功能但在该子目录中查找迁移的rake任务。可能不是超级优雅但它有效。您可以复制并粘贴已经在rails中的rake任务,只需稍微修改它们即可。
基于@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
如果你想在你的rails网站上显示wordpress帖子,你不想使用多魔法连接gem。您可以使用以下代码从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
我通过为不同的数据库创建单独的连接器类并在迁移中使用它来实现此目的。
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保留一个连接池,该连接池是由类索引的哈希。 Read more here。因此,使用单独的类进行单独的连接可以保护我们免受关闭的连接错
此外,使用up
和down
而不是change
允许我们回滚迁移没有任何问题。仍然没有想出这个的原因。
有一个更简单的答案。将此添加到您的迁移中:
def connection
ActiveRecord::Base.establish_connection("quiz_#{Rails.env}").connection
end
那是Rails 3.1。对于Rails 2.X或3.0,它是一个类函数(例如def self.connection
)
我使用以下代码使用它。
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表跟踪已检入相应项目的版本控制的迁移。
我无法让下行迁移工作。
您应该在/ config / environments中定义其他数据库/环境。
之后,您可以使用以下命令迁移该特定环境。
rake db:migrate RAILS_ENV=customenvironment
继@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
我最近一直在努力解决同样的问题。目标是将历史表分割到不同的数据库,因为它已经很大并且仍在快速增长。
我开始尝试通过执行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表,以便不再运行迁移。
嘿,我已经深入研究了几天,我最终得到了这个解决方案,只想分享它,它可能对某人有所帮助。
这里有完整的要点。 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基类继承的模型上连接它。 (在要点中的例子)。
最好!。
对于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
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环境的db的连接。
如果您只想在迁移中使其可用,则会扩展ActiveRecord::Migration
类。