Sunday, October 26, 2008

Connecting to Multiple Databases in Ruby on Rails

If you keep different tables in different databases, you've probably been looking for a way to allow Rails to connect to more than one database. You've probably found that you can add another configuration (besides development, test and production) to config/database.yml. There's a problem with that though. If you added a configuration called foo, you are now always connecting to foo, in production mode, test mode and development mode. This is especially bad in test mode, since the database would get wiped, but you probably want a different database for development and production too.

Here's how to do it. Suppose you have already set up your regular development/test/production databases in config/database.yml:

development:
  adapter: jdbc
  driver: com.mysql.jdbc.Driver
  url: jdbc:mysql://devdb.yourdomain.com:3306/mstore
  username: cart
  password: secret

# Warning: The database defined as 'test' will be erased and
# re-generated from your development database when you run 'rake'.
# Do not set this db to the same as development or production.
test:
  adapter: jdbc
  driver: com.mysql.jdbc.Driver
  url: jdbc:mysql://localhost:3306/mstore
  username: cart
  password: secret

production:
  adapter: jdbc
  driver: com.mysql.jdbc.Driver
  url: jdbc:mysql://proddb.yourdomain.com:3306/mstore
  username: cart
  password: secret

Now you decided to store all the user account information in a different database. You still want a separate development, test and production databases for your account information. You'll need to add three more entries to the database configuration then, one for each of the account databases. You'll need to name them with an appropriate suffix to specify development/test/production:

account_development:
  adapter: jdbc
  driver: com.mysql.jdbc.Driver
  url: jdbc:mysql://devdb.yourdomain.com:3306/account
  username: cart
  password: secret

# Warning: The database defined as 'test' will be erased and
# re-generated from your development database when you run 'rake'.
# Do not set this db to the same as development or production.
account_test:
  adapter: jdbc
  driver: com.mysql.jdbc.Driver
  url: jdbc:mysql://localhost:3306/account
  username: cart
  password: secret

account_production:
  adapter: jdbc
  driver: com.mysql.jdbc.Driver
  url: jdbc:mysql://accountdb.yourdomain.com:3306/account
  username: cart
  password: secret

Note that these account databases could be on the same hosts or on different hosts from the main databases. Now on to the models. You need to create a base class model for all your account tables, so that only one connection to the account database gets created. If you just call establish_connection in every model, it'll open that many database connections, one for each model. Here's a base model for the account tables:

class AccountBase < ActiveRecord::Base
  # No corresponding table in the DB.
  self.abstract_class = true

  # Open a connection to the appropriate database depending
  # on what RAILS_ENV is set to.
  establish_connection("account_#{RAILS_ENV}")
end

Now your account models need to inherit from AccountBase instead of ActiveRecord::Base, like this:

class Login < AccountBase
end

Because it inherits from AccountBase, it'll use the database connection already established by AccountBase.

3 comments:

Shad Sharma said...

Great info here. Thanks.

Sleepless said...

This is a great post but missing some info. I generate my model, then add the new base class, when I run rake db:migrate my tables end up in my original database.

When I try to access the controller I get a message saying "database not configured" (presumably because its trying to find my tables in the newer, specialized database)

Any hints? How do I get rake to put my tables in the new database?

Gary said...

I've never tried running a migration on a different database. The migration doesn't use your model, and so ignores your establish_connection. You might have to do something like this:

http://stackoverflow.com/questions/1404620/using-rails-migration-on-different-database-than-standard-production-or-develo