44

I need to use different database connections in different Rails models. Is there a not-so-hacky way to do that?

Any links or search keywords would be great :)

Nathan Long
  • 122,748
  • 97
  • 336
  • 451
gustavgans
  • 5,141
  • 13
  • 41
  • 51
  • Do you mean that different fields on a single model actually come from different databases or just that different models within the application come from different databases? – mikej Aug 04 '09 at 07:58
  • different models from different database servers – gustavgans Aug 04 '09 at 08:12
  • @gustavgans - I edited your question to make it a bit clearer, based on your reply to `mikej`. – Nathan Long Jul 14 '11 at 16:03

7 Answers7

116

Add new sections to your database.yml e.g.

other_development:
  adapter: mysql
  database: otherdb_development
  username: root
  password:
  host: localhost

other_production:
  adapter: mysql
  database: otherdb_production
  username: root
  password:
  host: localhost

Add a class in lib/other_database.rb

class OtherDatabase < ActiveRecord::Base
  establish_connection "other_#{RAILS_ENV}"
end

and then for each model which isn't in the default database subclass from OtherDatabase e.g.:

class MyModel < OtherDatabase
   # my model code...
end
ocodo
  • 29,401
  • 18
  • 105
  • 117
mikej
  • 65,295
  • 17
  • 152
  • 131
13

I have been using the following to connect to 2 db in the same app. I put them in lib folder since everything in there is loaded.

require 'active_record'

class OldDatabase < ActiveRecord::Base
  self.abstract_class = true
  establish_connection(
  :adapter  => 'mysql',
  :database => 'weather',
  :host     => 'localhost',
  :username => 'root',
  :password => 'password'
  )
end

class NewDatabase < ActiveRecord::Base
  self.abstract_class = true
  establish_connection(
  :adapter  => 'mysql',
  :database => 'redmine',
  :host     => 'localhost',
  :username => 'root',
  :password => 'password'
  )
end

class WeatherData < OldDatabase
end

class Board < NewDatabase
end

Hope that helps

penger
  • 3,861
  • 1
  • 16
  • 11
  • I needed to migrate data from an old app to a new app in a Rake task, and used a similar approach. The difference was that, because the Rake tasks uses `:task_name => :environment`, it has the Rails environment available, so the model in the new app can just be used as normal and pulls from database.yml. Only the model for the old app needs to have its info defined as you show. – Nathan Long Jul 14 '11 at 16:00
10

mikej is right. I did however write a gem that makes the model code to connect a little bit cleaner, check it out.

Randy Eppinger
  • 1,054
  • 13
  • 23
nitecoder
  • 5,496
  • 1
  • 28
  • 35
9

Update for Rails 3.x:

class MyModel < ActiveRecord::Base
  establish_connection "other_#{Rails.env}"
end
penkovsky
  • 893
  • 11
  • 14
4

Rails 6 added native support for multiple databases: https://edgeguides.rubyonrails.org/active_record_multiple_databases.html

database.yml

development:
  one:
    <<: *default
  other:
    <<: *default

Model base classes:

class OneModelBase < ActiveRecord::Base
  around_action :set_db

  private

  def set_db
    ActiveRecord::Base.connected_to(database: :one) do
      yield
    end
  end
end

class OtherModelBase < ActiveRecord::Base
  around_action :set_db

  private

  def set_db
    ActiveRecord::Base.connected_to(database: :other) do
      yield
    end
  end
end

You'll also have different migrations and schemas per DB. Running a command like rails db:create will create all databases. You can scope commands, e.g. rails db:create:other.

thisismydesign
  • 21,553
  • 9
  • 123
  • 126
  • So with this setup, running `rails db:migrate` will run migrations on all databases, right? And would the app, when a request comes in, connect to all DBs, or is there a way to stipulate at runtime which DB to connect to? – Mitya Aug 01 '22 at 20:13
1

I think that the prettiest way to connect to another database with active model is creating base class for external database, and then inherite from that base in your model. This method works fine with rails 4.2.6 and 5.0.4

For example:

# in /models/external_db/base.rb
require 'active_record'

class ExternalDb::Base < ActiveRecord::Base
  self.abstract_class = true
  establish_connection "external_db_#{Rails.env}".to_sym
end

And in your model class:

# in /models/external_db/some_model.rb
class ExternalDB::SomeModel < ExternalDb::Base
  # your code
end

But you must define external database in /config/database.yml

# in /config/database.yml
external_db_development:
  adapter: sqlite3
  pool: 5
  timeout: 5000
  database: db/external_db_development.db

external_db_production:
  adapter: sqlite3
  pool: 5
  timeout: 5000
  database: db/external_db_production.db
Adrian
  • 490
  • 5
  • 7
0

In rails 4.1+ establish_connection now takes a symbol:

class OtherDbModel < ActiveRecord::Base
  establish_connection :"other_#{Rails.env}"
end
Vasfed
  • 18,013
  • 10
  • 47
  • 53