在一个rails应用中管理多个数据库

作者 陈越 日期 2020-09-18
在一个rails应用中管理多个数据库

start

最近有一个任务,需要在rails应用中引用项目外的数据库,最开始只是通过ActiveRecord的establish_connection方法连接项目外的数据库,然后封装成另一个module下的一批model来使用,如果仅仅是为了crud,这样是一种比较常用的做法,但如果想在自己的应用里对项目外的数据库进行管理呢,如建表建字段等操作,或者想在自己的rails应用里管理两个数据库等,就需要用下面的方式了。

1. 新建外部数据库的配置文件

# config/second_database.yml
default: &default
adapter: postgresql
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
timeout: 5000
encoding: utf8
host: <%= ENV.fetch("DATA_HOST") { "localhost" } %>
port: <%= ENV.fetch("DATA_PORT") { 5432 } %>

development:
<<: *default
database: second_development

test:
<<: *default
database: second_test

production:
<<: *default
database: second_production

2. 新建db_second文件夹

在项目根目录下创建管理migrate文件的文件夹db_second,文件夹中的内容可以参考应用的db文件夹

3. 新建db_second的rake文件

为了在管理项目外数据库的时候可以用类似rails db:create/rails db:migrate等命令,我们需要添加一个rake文件,并通过这个文件定义的rake task来管理db_second

# lib/tasks/db_second.rake
task spec: ['second:db:test:prepare']

namespace :second do
namespace :db do |ns|
%i[drop create setup migrate rollback seed version].each do |task_name|
task task_name do
Rake::Task["db:#{task_name}"].invoke
end
end

namespace :schema do
%i[load dump].each do |task_name|
task task_name do
Rake::Task["db:schema:#{task_name}"].invoke
end
end
end

namespace :test do
task :prepare do
Rake::Task['db:test:prepare'].invoke
end
end

# 向上面定义的所有任务追加和添加适当的任务
ns.tasks.each do |task|
task.enhance ['second:set_custom_config'] do
Rake::Task['second:revert_to_original_config'].invoke
end
end
end

task :set_custom_config do
# save current vars
@original_config = {
env_schema: ENV['SCHEMA'],
config: Rails.application.config.dup
}

# set config variables for custom database
ENV['SCHEMA'] = 'db_second/schema.rb'
Rails.application.config.paths['db'] = ['db_second']
Rails.application.config.paths['db/migrate'] = ['db_second/migrate']
Rails.application.config.paths['db/seeds.rb'] = ['db_second/seeds.rb']
Rails.application.config.paths['config/database'] = ['config/second_database.yml']
end

task :revert_to_original_config do
# reset config variables to original values
ENV['SCHEMA'] = @original_config[:env_schema]
Rails.application.config = @original_config[:config]
end
end

这个时候使用rails second:db:create就能看到如下效果:

$ rails second:db:create
Created database 'second_development'
Created database 'second_test'

并且会生成db_second/schema.rb文件

4. 新建db_second_migration生成器

为了使用类似rails g migration xxxx的功能来创建项目外数据库的迁移管理文件,我们必须新增一个属于项目外数据库的迁移生成器,并让这个生成器继承于ActiveRecord::Generators::MigrationGenerator

# lib/generators/device_index_migration_generator.rb
require 'rails/generators/active_record/migration/migration_generator'

class SecondMigrationGenerator < ActiveRecord::Generators::MigrationGenerator
migration_file = File.dirname(
ActiveRecord::Generators::MigrationGenerator
.instance_method(:create_migration_file)
.source_location.first
)

source_root File.join(migration_file, "templates")

def create_migration_file
set_local_assigns!
validate_file_name!
migration_template @migration_template, "db_second/migrate/#{file_name}.rb"
end
end

这时候就可以使用rails g second_migration xxxx的命令去创建项目外数据库的迁移文件了

end