polymorphic ポリモーフィック関連
使用可能な関連付け
belongs_to
対となる関連に指定するオプション
- as ポリモーフィック関連外部キー名指定
has_many
has_one
概要
例えば、BookのauthorとしてPerson(個人)とGroup(団体)のいずれかを指定できるようにしたい場合に利用できるのがポリモフィック関連である。
book = Book.new
book.author = Person.last
book.author.class # => "Person"
book.author = Group.last
book.author.class # => "Group"
定義
マイグレーションファイルを以下のように記述する。
class CreateBooks < ActiveRecord::Migration[8.0]
def change
create_table :books do |t|
t.references :author, null: false, polymorphic: true
t.timestamps
end
end
end
references
のカラムにpolymorphic: true
を指定することで、当該カラムがポリモフィック関連のカラムになる。外部キー制約(foreign_key: true
)との併用できない。この点については後述する。
モデルの定義は以下のようにする。
class Book < ApplicationRecord
belongs_to :author, polymorphic: true
end
これで、「概要」に書いたような例を利用できるようになる。
外部キー制約
外部キー制約(foreign_key: true
)との併用できない。これはauthor_id
とauthor_type
の2カラムで複数の関連先と繋ぐ仕組みになっているため。
Person.create # => id: 1
Group.create # => id: 1
Group.create # => id: 2
Book.create(author: Person.find(1)) # => author_id: 1, author_type: "Person"
Book.create(author: Group.find(2)) # => author_id: 2, author_type: "Group"
ただし、belongs_to
のデフォルトの挙動で、関連先がない場合はバリデーションエラーとなる。
# Person.find(100000) がActiveRecord::RecordNotFoundならバリデーションエラー
Book.create(author_id: 100000, author_type 'Person')
しかし、以下のような使い方をした場合はバリデーションでチェックされないので注意する必要がある。
person = Person.last
book = Book.new(author: person)
Person.last.destory # => 関連先レコードを削除
book.save # => true
この挙動はvalidates :author, presence: true
を書いても止められないため、確実にチェックしたいのであればvalidate
を追加する必要がある。
validate do
unless author_type.constantize.exists?(author_id)
errors.add(:author, :blank)
end
end