ActiveRecord でモデルごとに別のデータベースを使う方法
2007/03/28
ある Rails アプリケーションで他の Rails アプリケーションのデータベースを参照したかったので、方法を調べました。情報源は以下のサイトです。
ActiveRecord の API ドキュメントにはこう書いてあります。
Connections are usually created through ActiveRecord::Base.establish_connection and retrieved by ActiveRecord::Base.connection. All classes inheriting from ActiveRecord::Base will use this connection. But you can also set a class-specific connection. For example, if Course is a ActiveRecord::Base, but resides in a different database you can just say Course.establish_connection and Course *and all its subclasses* will use this connection instead.
データベースへのコネクションは ActiveRecord::Base.establish_connection で作られるので、あるモデル(例えば Course)で別のコネクションを使いたければ、Course.establish_connection を自分で呼べばよいらしい。ということでやってみました。
今回は Rails で作られたブログシステム Typo のデータベースを独自アプリケーションから参照するという想定ですが、別に Typo に限った話ではないので適当に読み替えてください。
Typo のデータベースは以下になっています。
- DBMS:
- MySQL
- DB 名:
- typo
- ユーザ:
- user
- パスワード:
- password
Typo は Rails で作られているので、テーブル名やカラム名は Rails の規約に則っています。
では、Typo のテーブルからユーザの情報を参照してみます。まずは database.yml に Typo のデータベース定義を追加します。
[nice_code]typo:
adapter: mysql
database: typo
username: user
password: password
socket: /var/lib/mysql/mysql.sock[/nice_code]
次に、参照するテーブルのモデルを作ります。ユーザの情報はデータベース typo の users テーブルに入っていますので、自分のアプリケーションにも User モデルを作ります。マイグレーションファイルは不要なので作成しないようにします。
[nice_code]$ script/generate model user –skip-migration[/nice_code]
ここで User モデルの establish_connection を直接使ってもいいですが、そうすると、他にもモデルを追加したくなった場合に、それぞれのモデルで establish_connection する必要があり、DRY ではありません。Typo のデータベースを参照するモデル用のスーパークラス TypoModel を作って、TypoModel の establish_connection を使うようにします。これで TypoModel のサブクラスは Typo のデータベースを参照するようになるはずです。User モデルも TypoModel を継承します。
[nice_code]$ script/generate model typo_model –skip-migration[/nice_code]
TypoModel は以下のようにします。
[nice_code]class TypoModel < ActiveRecord::Base
self.abstract_class = true
establish_connection ‘typo’ # database.yml に追加したデータベース定義名を指定します
end
[/nice_code]
ここで、
[nice_code]self.abstract_class = true[/nice_code]
としているのは、TypoModel が抽象クラスで、対応するテーブルが存在しないことを指定するためです。この記述がないと、「typo_models なんてテーブルはないぞ」というエラーが出ます。
User モデルは以下のようにして TypoModel を継承します。
[nice_code]class User < TypoModel
end
[/nice_code]
以上で完了です。確認してみます。
[nice_code]$ script/console
>> User.find(:all)
=> [#<User:・・・・・・ # データベース typo の users テーブルからモデルを取得できた
>> Hoge.find(:all)
=> [#<Hoge:・・・・・・ # 自分のアプリケーションのテーブルから Hoge モデルを取得できた
[/nice_code]
読めました。
補足。自分のアプリケーションにすでに User モデルが存在するなどの理由で、モデル名を別の名前にしたい場合は、モデルのクラス名を TypoUser などにして set_table_name でテーブル名を users にしてもいいと思います。モデルが何らかの関連を持つ場合は、外部キーのカラム名と合わなくなるため、関連の定義も修正する必要があるでしょう。
ただし、勝手に名前を変えるとはまる場合もあります。例えば、モデルが Single table inheritance を利用している場合、テーブルの type カラムにモデルのクラス名が入っているため、クラス名を変えてしまうと Single table inheritance が働かずにはまります。hara ははまりました。