0
0

【Rails】一つのモデルに対して複数の意味を持たせたい(class_name, inverse_of )

Last updated at Posted at 2024-07-07

実現させたいこと

例えばユーザー(User)と本(Book)のモデルが存在している時
Userに二つの意味を持たせたいことがある

  • 本を所有しているユーザー(User)
  • 本の著者(Author)
book.author
# => 本の著者を取得
book.user
# => 本の所有者を取得

同じUserモデルを使って、author、userをそれぞれ取得できる

モデル作成

マイグレーションファイルは以下のようにBookに外部キーとして

  • user_id
  • author_id

を持たせます

class CreateBooks < ActiveRecord::Migration[7.1]
  def change
    create_table :books do |t|
      t.belongs_to :user, null: false
      t.belongs_to :author, null: false

      t.timestamps
    end
  end
end

モデルは下記のようにclass_nameオプションを指定。

# ユーザーモデル
class User < ApplicationRecord
  has_many :books, inverse_of: :author
end

# 本モデル
class Book < ApplicationRecord
  # 本の所有者
  belongs_to :user
  # 本の著者
  belongs_to :author, foreign_key: "author_id", class_name: "User", inverse_of: :books
end

上記を設定することで、
book.userで本の所有者を取得できるのはもちろん、
book.authorで本の著者を取得できるようになる。

inverse_ofについて

この時登場するのが双方向関連付けを宣言するための inverse_ofです。

railsでは元々何もしなくても双方向関連付けが設定されていますが、
今回のようにUserモデルに別の意味を持たせるよう変更を加えた場合、別途指定が必要になります。

双方向関連付け確認方法

双方向関連付けとは・・・
二つのモデル間でお互いのデータを参照したり、更新したりするとき、データの齟齬が起こらない。
別のテーブル経由で値を参照しても同じ結果が得られる。
(例えば user.name == book.author.name)
(例えば book.title == user.books.first.title)

双方向関連付けを行なっていないと、
更新したはずのUserの情報が更新されていないみたいなことが起こります。

👇関連付けできていない例

user = User.first
book = user.books.first

user.name
# => "ユーザー1"
book.author.name
# => "ユーザー1"

user.name = "ユーザー2" # userオブジェクトのnameを変更

user.name
# => "ユーザー2"
book.author.name 
# => "ユーザー1" (←ユーザー2の出力されるはず????)

原因としては、user.namebook.author.name で取得できるオブジェクトは一見同じように見えるが、
実は異なるオブジェクト

user.object_id
# => 11111
book.author.object_id
# => 12121

実はオブジェクトidが異なる

👇関連付け成功例

user = User.first
book = user.books.first

# オブジェクトidが一致している
user.object_id # => 1234
book.object_id # => 1234

user.name = 'ユーザー1'

user.name
# => 'ユーザー1'
book.author.name
# => 'ユーザー1'

# user.name と book.author.name が一致していればOK

参考

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0