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 ははまりました。

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト /  変更 )

Google フォト

Google アカウントを使ってコメントしています。 ログアウト /  変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト /  変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト /  変更 )

%s と連携中

%d人のブロガーが「いいね」をつけました。