Ruby On Rails, Design, Simplicity, Web 2.0, Ajax, Mac and Tons of Pizza.

Dec 11

Multiple database handling with Rails

Posted by Sandro Paganotti in Ruby on Rails - comments are closed digg this add to delicious

What happen if your chief ask you to write an application that use more than one database? Wow… sound scaring, how to tell ActiveRecord that some models belong to a database and others to another? You’ll be surprised ‘cause it is quite easy.

Let’s start:

1. new database description:

Open your database.yml file. Under the three standard databases (development, test and production) add the informations needed to connect to you extra db. Use the standard yml sintax like:


extradb_development:
  adapter:     mysql
  host:        localhost
  username:    root
  password:    blabla
  database:    extradb_dev

extradb_test:
  adapter:     mysql
  host:        localhost
  username:    root
  password:    blabla
  database:    extradb_test

extradb_production:
  adapter:     mysql
  host:        localhost
  username:    root
  password:    blabla
  database:    extradb_prod


2. Create models

Create models using the command line tool as usual, just remember to delete the migration associated.


ruby script/generate model cat 
ruby script/generate model dog

3. Associate models with extradb

You need to manually overwrite the estabilish_connection method for the models linked to the external database. Doing this in ‘application.rb’ using a ‘before_filter’ callback ensure that the connection will be always present. The code is quite simple:


class ApplicationController < ActionController::Base  
  require 'yaml'

  before_filter :set_extra_db_connection

  def set_extra_db_connection
   extra_coord = YAML.load(File.open(File.join(RAILS_ROOT,"config/database.yml"),"r"))["extradb_"+ ENV['RAILS_ENV']]
   Cat.establish_connection(extra_coord)
   Dog.establish_connection(extra_coord)
  end

end


4. Done!

Yess! You made it! Now you can use your ‘foreign models’ exactly like the normal ones. You can use validations and also relationship (also between model from different databases!).

Easy isn’t it?

Comments

  • planetmcd

    Posted on December 11

    I know very little about the inner workings of Rails, but if this is set in the application.rb, with a before filter, is it instantiating the connection on every request?
  • Sandro Paganotti

    Posted on December 12

    Thanks for the reply planetmcd. Unfortunately yes, to avoid this you can retrieve the informations about your extra-db connection in a global variable and use 'estabilish_connection' in the models. Or ( better ) you can create an abstract model class that manages the connection and extend this class from the models Cat and Dog.
  • Ghislain

    Posted on December 13

    Thanks for this useful tutorial ! I just noticed a small error : The good line to put is : extra_coord = YAML.load(File.open(File.join(RAILS_ROOT,"config/database.yml"),"r"))["extradb_"+ ENV['RAILS_ENV']]
  • Sandro Paganotti

    Posted on December 13

    Thank for your suggest Ghislain. I've edited the post to fix the problem :) See you.
  • Joe Martinez

    Posted on December 19

    Nice post. :)
  • Anthony Eden

    Posted on January 06

    Please don't take this the wrong way, but your suggestion has a number of problems in it. First of all you're establishing the connection in the controller...what happens when you use the model outside of a controller, for example in an external script, in unit tests, or anywhere else where the controller is not involved? Your model will fall back to its normal connection. Recipe #15 from Rails Recipes covers how to do this by having classes extend from a base model which establishes the connection. This is a better approach because it means a model can be used anywhere and it will use the alternate connection...well, almost...on to another problem: Fixtures don't work with multiple databases at this point without patching Rails. You are unit testing your models, right? You will see this issue crop up if you do and you use fixtures. In the case of your code it would break even if fixtures did support multiple databases due to the first problem addressed. Anyhow, the following tickets talk about the fixture issue: http://dev.rubyonrails.org/ticket/5381 and http://dev.rubyonrails.org/ticket/5382
  • Sandro

    Posted on January 06

    Thank you Anthony for the comment :). I'm actually working on refactoring this tutorial using your very useful suggestion ! Sandro Paganotti.

Post a comment

Categories:

Tags:

Powered by Mephisto, Valid XHTML 1.1, Valid CSS - Supported by Wave Factory