Ruby on Rails:belongs_to, has_many で関連付けを行う

ターゲットするER図

image.png

belongs_to, has_many で関連付けを行う

上記のER図において、『日報は投稿者しか更新削除できない』というようにしたい。そのためには日報にユーザ情報が必要なので、日報テーブルにユーザIDの外部キーを導入する。

以下では、日報テーブルにユーザIDの外部キー以外を登録してあったテーブルに対して、外部キーを追加する流れを記述。

と言うわけで、日報テーブルにユーザIDカラムを追加するために、まずはマイグレーションファイルを rails g コマンドで大枠を作る。

rails g migration AddUserIdToReports

生成されたマイグレーションファイルに、ユーザーIDの外部キーを追加するコードを書く。

class AddUserIdToReports < ActiveRecord::Migration[6.1]
  def change
    add_reference :reports, :user, foreign_key: true
  end
end

そして、データベースを移植する。

% rails db:migrate
Running via Spring preloader in process 87420
== 20220906140109 AddUserIdToReports: migrating ===============================
-- add_reference(:reports, :user, {:foreign_key=>true})
   -> 0.0069s
== 20220906140109 AddUserIdToReports: migrated (0.0069s) ======================

ここまでで、ユーザID用を追加した日報テーブルが用意できた。次は、日報とユーザの関連を定義するために、日報モデルに belongs_to で関連付けを行う。 1つの日報は、特定のユーザに紐づくため :user は単数系。

# app/model/report.rb
class Report < ApplicationRecord
  belongs_to :user
end

関連付けを行うことで、ユーザIDがないと日報が作れないようになる。

image.png

次は、ユーザ情報を日報インスタンスに紐づける必要がある。

試しにコンソール上で少し試してみる。下記のように、現状はユーザインスタンスから日報メソッドを操作することができない。

image.png

紐づいているユーザで日報を作成するために、 has_many で複数所有の関連付けを行う。ここで、ユーザが消滅したら日報も同時に消滅させるために、:destroy タグをつけておく。ちなみに複数所有するので、 reports と複数形になっている。

# app/models/user.rb
...
has_many :reports, dependent: :destroy
...

上記の設定で、ユーザインスタンスを通して日報メソッドを操作できるようになった。

image.png

ここまでできたら、最後はユーザインスタンスを通して、日報オブジェクト生成をしてあげられるようにする。

ビューで呼び出される create メソッドを下記のように変更する。

# app/controllers/reports_controller.rb
...
# POST /reports or /reports.json
  def create
    # 変更前
    @report = Report.new(report_params)
    # 変更後
    @report = current_user.reports.build(report_params) 
    # 変更後(new も使えるらしい。が、慣習的にbuildを使うらしい)
    # @report = current_user.reports.new(report_params)
...

試しに入力する。

image.png

無事完了🤗

あとは、投稿したユーザのみ日報を更新・削除できると言う部分について。

日報一覧と日報詳細ビューに if report.user == current_user みたいな感じで、ログインユーザとレポートユーザが一致するとき、更新・削除ボタンを表示するに変更した。

image.png

参考