Rails 6 で 2つ(複数)のデータベースを用いる方法

前提

Railsガイドで解説されている、レプリカとかリードオンリーとかは考えず、単に2つのデータベースをモデルに応じて使い分けることを目的とします*1

手順

1. config/database.yml を編集する

まず config/database/yml を編集します。データベースが1つの場合には、production:development: の下の階層にデータベースの接続設定を書きますが、複数のデータベースを用いる場合は、それらのデータベースの接続設定に名前を付けてあげて、その名前のひとつ下の階層にデータベースの接続設定を書きます。

また、それぞれの接続設定に対して migrations_paths を書く必要があります。データベースごとに、適用されるマイグレーションの場所を分けます。

YAMLはたとえば、以下のように書きます。

default: &default
  adapter: postgresql
  encoding: unicode
  pool:     <%= ENV.fetch('RAILS_MAX_THREADS') { 5 } %>
  host:     <%= ENV.fetch('PG_HOST', 'localhost') %>
  port:     <%= ENV.fetch('PG_PORT', '5432') %>
  username: <%= ENV.fetch('PG_USER', 'ANATA_NO_NAMAE') %>
  password: <%= ENV.fetch('PG_PASSWORD', 'ANATA_NO_PASSWORD') %>

production:
  tomato:
    <<: *default
    database: tomato_no_database_production
  melon:
    <<: *default
    database: melon_no_database_production
    migrations_paths: db/melon_no_migrate

development:
  tomato:
    <<: *default
    database: tomato_no_database_development
  melon:
    <<: *default
    database: melon_no_database_development
    migrations_paths: db/melon_no_migrate

test:
  tomato:
    <<: *default
    database: tomato_no_database_test
  melon:
    <<: *default
    database: melon_no_database_test
    migrations_paths: db/melon_no_migrate

上記の書き方により、tomatomelon の2つのデータベースの接続設定が作られました。

2. app/models 配下に、第2のデータベースを司るモデルファイルを作る

データベースが1つの場合には app/models/application_record.rb が、モデルのファイルのすべてを司るファイル(全てのモデルファイルが継承をするファイル)でした。データベースが2つの場合は、2つ目のデータベースを司るためのファイルを作ります。ここがポイントかと思います。

2-1. ファイル名をどうするか

まずファイル名をどうするかですが、app/models 配下には大量のモデルファイルが作られるため、それらと区別できるようなファイル名にする必要があります*2。Railsガイドでは HogehogeBase というように Base を接尾語として付与しています。

個人的には、application_record.rb という既存のファイルにならって、application_hogehoge_record.rb のようにするほうが、ファイル一覧の並びでの視認性という意味でもわかりやすいかなと思っています。

2-2. 抽象クラスとなるモデルのファイルを作る

ファイル名が決まったら、内容を以下のように書きます。ファイル名を application_hogehoge_record.rb としたと仮定します。

class ApplicationHogehogeRecord < ApplicationRecord
  self.abstract_class = true

  connects_to database: { writing: :melon, reading: :melon }
end

ApplicationHogehogeRecordApplicationRecord を継承することに注意してください*3

そして、connects_to メソッドで、このモデルが接続するデータベースを決めます*4

database: { writing: :melon, reading: :melon } という引数を与えることで、ApplicationHogehogeRecord モデルは、config/database.yml で命名した、melon というデータベースに接続することになります。

writingreading は、CRUD の内容ごとに接続するデータベースをさらに区別する場合の指定ですが、今回は冒頭の「前提」の下での話になるので同じ値である :melon を指定しています。レプリケーションとかラウンドロビンとかを考える場合には細かく設定するのがいいでしょう。

2-3. 作った抽象クラスを継承したモデルファイルを作る

第2のデータベースを用いるモデルファイルは、application_record.rb ではなく、前述の application_hogehoge_record.rb を継承します。

たとえば、foobar.rb というモデルファイルが第2のデータベースを用いる場合には、app/models/foobar.rb の内容は次のようになります。

class Foobar < ApplicationHogehogeRecord
end

さて、rails g model でモデルファイルを作る場合には、--database オプションを用いて例えば --database melon のように指定することで、作成されるマイグレーションファイルが config/database.yml で指定したディレクトリのはいかに作成されます。

ただし、留意しておくこととしては、application_hogehoge_record. rb を継承したファイルを rails g model で作る場合には制限があるということです。継承するモデルを application_record.rb 以外にする場合は --parent オプションを用いるのですが、このオプションを用いるとマイグレーションファイルが生成されません

所定のディレクトリ配下*5に手動でマイグレーションファイルを作る必要があります*6。これは、--parent オプションを使用する場合は STI の実装を前提としているからです。以下の Stack Overflow のとおりです。

--parent オプションを使わないで rails g model を実行すると、生成されたクラスは ApplicationRecord を継承しているので、その部分を手動で ApplicationHogehogeRecord に書き換える必要があります。

どちらの方法を採るにしてもひと手間入りますので注意が必要です。これをいい感じにしてくれるコードを書いてもいいと思いますが*7、まあ大した手間ではないので手動で良いかなと思います。

3. あとは普段どおりにモデルを扱う

上記の設定ができたならば、あとは特に意識すること無くモデルを扱えます。モデルに応じて用いられるデータベースが自動で選択されます。

一度設定すれば、あとは意識せずに操作できるでしょう*8

4. db:migrate

rails db:migrate を行うと、config/database.yml に書かれている所定のディレクトリにあるマイグレーションファイルを判断して、自動でデータベースを割り振ってマイグレーションを行ってくれます。スキーマファイルもデータベースごとに作られます*9

特定のデータベースだけにマイグレーションしたい場合には、$ rails db:migrate:melon のようにして末尾に :HOGEHOGE とデータベース接続設定名*10を指定します。

補足

  • 異なるデータベース同士でもアソシエーションを(無意識に)用いることができます
    • データベースレベルで制約をつけるのは別の話になります
  • 3つ以上のデータベースを用いる場合には上記の方法を応用すればいいでしょう

*1:ActiveRecord::Middleware::DatabaseSelector

*2:区別できなくても問題ないといえばないのですが……

*3:ここがポイントかと思います

*4:「このモデルが接続する」とありますがこのモデルは抽象クラスなので、このクラスを継承したクラスが接続するデータベースを実質的に決めることになります

*5:config/database.yml 内で migrations_paths: によって指定した場所

*6:rails g migration する際には --database オプションが必要です

*7:すでにあるかも

*8:ただしデータベースをまたいだリレーションについては厳しいものがありますが……

*9:melon_schema.rb のように、第2のデータベースのスキーマファイルには接頭語が付与されます

*10:config/database.yml に書かれている

Powered by はてなブログ