はじめに
開発チームのメンバーとともに、Railsを利用して外部DBの絡む案件開発を進めていました
問題点
既存の外部DBからActiveRecordを利用してデータ取得を行いたいが、方法が分からないという場面に遭遇しました
解決法
database.ymlへの追記
まず、database.ymlに外部DBについての記述を追加する必要がありました。
今回は「外部DBが、アプリケーションで利用しているDBと同じRDSインスタンス内に存在している」という状況でしたので、以下の記載例のような形で接続情報を共有している状態とします。
また今回はデータ取得のみが目的で、Railsから外部DBを管理したいという意図はなかったため、database_tasks:false
を設定しました。
default: &default
adapter: mysql2
encoding: utf8mb4
username: user
password: password
host: host
development:
app:
<<: *default
database: app_development
external:
<<: *default
database: external_db
database_tasks: false
test:
app:
<<: *default
database: app_test
external:
<<: *default
database: external_db
database_tasks: false
production:
app:
<<: *default
database: app_production
external:
<<: *default
database: external_db
database_tasks: false
外部DBが完全に別物である場合(こちらの場面の方が多いかと思います)、例えば以下のように接続情報を分けて記載することで対応が可能なようです。
main: &main
adapter: mysql2
encoding: utf8mb4
username: user
password: password
host: host
external: &external
adapter: mysql2
encoding: utf8mb4
username: external_user
password: external_password
host: otherhost
database_tasks: false
development:
app:
<<: *main
database: app_development
external:
<<: *external
database: external_db
test:
app:
<<: *main
database: app_test
external:
<<: *external
database: external_db
production:
app:
<<: *main
database: app_production
external:
<<: *external
database: external_db
外部DB用抽象クラスの作成
database.ymlへの記載が完了したら、以下のように新たな抽象クラスを作成...というと私はすぐにピンと来なかったのですが、いわば「外部DB用のApplicationRecord」のような存在を作成してあげることで各種取り回しが容易となります。
class ApplicationExternalRecord < ApplicationRecord
# 引き継ぎたくないものがある場合、継承元はActiveRecord::Baseに
# 抽象クラスとして設定
self.abstract_class = true
# 外部DBを使用するように設定(database.yml内の記述と対応)
establish_connection :external_db
# DBのライター/リーダーのエンドポイントが明確に分かれている場合などは以下の記法が適している
# connects_to database: { writing: :external_db, reading: :external_db }
end
外部DBの各テーブルに対応するmodelの作成
先ほど作成した抽象クラスを継承する形で外部DB上のお目当てのテーブルに対応するmodelを作成していきます。
ここまでの作業が上手く行けば、例えばExternalTest.all
といった記述によるデータ取得が可能となります。
必要であれば、外部DBと紐付けたmodel同士の関連付けも設定可能です。
class ExternalTest < ApplicationExternalRecord
# デフォルトではクラス名に基づいて外部DB上のexternal_testテーブルを見に行こうとしてしまう..
# self.table_nameによってテーブル名を指定できる。テーブル名が大文字である場合などにも有効
self.table_name = 'TEST'
# has_many external_test_details
end
おわりに
今回の対応を通してdatabase.ymlの記法やApplicationRecord周りの挙動等、当初の問題と直接絡まない部分についても様々な学びを得ることが出来たと感じています🍀
参考にさせていただいた記事など