【Ruby on Rails】同じテーブルのリレーションを同一投稿にも保存する方法

こんにちは、nishi_talk(@nishi_talk)です。
Railsでリレーションを貼った投稿を作成することが多いですが、同じテーブルのリレーションを別でもたせる方法をご紹介します!




ちょっと特別な案件かもしれないが、ひとつの投稿に対して、同じリレーション先を登録必要があったので、対応した方法を紹介。
※既存案件だったので、DBをあまり触らず対応したので、他にもベストプラクティスがあると思うけど

やりたいこと

例えば、ひとつの投稿に対して編集者が投稿した月と管理者がその投稿を編集した月を分けて管理する必要があった。既に編集者が投稿した月にhas_manyでリレーションしていた。
後日、管理者がその投稿を編集した月を追加する。

posts(1) → monthlies(多)
mysql> show columns from posts;
+-----------------+--------------+------+-----+---------+----------------+
| Field           | Type         | Null | Key | Default | Extra          |
+-----------------+--------------+------+-----+---------+----------------+
| id              | bigint(20)   | NO   | PRI | NULL    | auto_increment |
| monthly_id      | bigint(20)   | YES  | MUL | NULL    |                |
+-----------------+--------------+------+-----+---------+----------------+

mysql> show columns from monthlies;
+-------------------+--------------+------+-----+---------+----------------+
| Field             | Type         | Null | Key | Default | Extra          |
+-------------------+--------------+------+-----+---------+----------------+
| id                | bigint(20)   | NO   | PRI | NULL    | auto_increment |
| data              | varchar(255) | YES  |     | NULL    |                |
+-------------------+--------------+------+-----+---------+----------------+

モデル

railsのモデルはこんな感じで、monthliesに対して、postが紐付いてる構造です。

app/models/monthly.rbに以下の記述をします。

class Monthly < ApplicationRecord
  has_many :posts
end



app/models/post.rbに以下の記述をします。

class Post < ApplicationRecord
  belongs_to :monthly
end

カラムを追加

まずは「posts」に「fix_monthly」を追加します。
fix_monthlyはpostのリレーションなので、referenceを指定します。

$ rails g migration AddPostRefToMonthly

db/migrate/xxx_add_monthly_ref_to_post.rbに以下の記述をします。

class AddMonthlyRefToPost < ActiveRecord::Migration[5.2]
  def change
    add_reference :posts, :fix_monthly
  end
end



作成できたら、migrateします。

$ rails db:migrate

データベースが変更されたか確認

postのテーブルにfix_monthly_idが追加されたか確認します。

mysql> show columns from posts;
+-----------------+--------------+------+-----+---------+----------------+
| Field           | Type         | Null | Key | Default | Extra          |
+-----------------+--------------+------+-----+---------+----------------+
| id              | bigint(20)   | NO   | PRI | NULL    | auto_increment |
| monthly_id      | bigint(20)   | YES  | MUL | NULL    |                |
| fix_monthly_id  | bigint(20)   | YES  | MUL | NULL    |                |
+-----------------+--------------+------+-----+---------+----------------+

mysql> show columns from monthlies;
+-------------------+--------------+------+-----+---------+----------------+
| Field             | Type         | Null | Key | Default | Extra          |
+-------------------+--------------+------+-----+---------+----------------+
| id                | bigint(20)   | NO   | PRI | NULL    | auto_increment |
| data              | varchar(255) | YES  |     | NULL    |                |
+-------------------+--------------+------+-----+---------+----------------+

これで一つの投稿に同じリレーションを貼ることができました。

コーディングにおすすめの本をご紹介