belongs_to/has_one/has_manyの簡単まとめ


Ruby on RailsのActive Recordでテーブル間の関連付け(アソシエーション)を行うメソッドbelongs_tohas_onehas_manyを簡単に説明します。

🎉 言いたいこと

  • 対象を1つ持っているなら、has_one
  • 対象を複数持っているなら、has_many
  • 自分が対象に所属しているなら、belongs_to

😸 自テーブルが対象に所属:belongs_to

belongs_toは、自分のテーブルが対象テーブルのレコードに所属する(対象テーブルのidカラムがある)場合に使います。

belongs_to(関連モデル名 [, scope, オプション])
オプション 説明
:class_name 関連するモデルクラス名を指定。関連名と参照先のクラス名を分けたい場合に使う
:foreign_key 参照先のテーブルの外部キーのカラム名
:primary_key 参照元(自分)のテーブルの外部キーのカラム名
:optional trueを指定すると参照先のテーブルのデータがない場合にエラーにならない

Rails 5からは外部キーのデータが存在しない場合はエラーになります。

🚜 自テーブルが対象を1つ持っている:has_one

has_oneは自分のテーブルが対象テーブルを1つ持っている(複数持たない)場合に使います。対象テーブル側に自分のidのカラムがある場合に使います。

has_one(関連モデル名 [, scope ,オプション])
オプション 説明
:class_name 関連するモデルクラス名を指定。関連名と参照先のクラス名を分けたい場合に使う
:foreign_key 参照先のテーブルの外部キーのカラム名
:primary_key 参照元(自分)のテーブルの外部キーのカラム名

👽 自テーブルが対象を複数持っている:has_many

has_manyは自分のテーブルが対象テーブルを複数もつ場合に使います。対象テーブル側に自分のidのカラムがある場合に使います。

has_many(関連モデル名 [, scope ,オプション])
オプション 説明
:class_name 関連するモデルクラス名を指定。関連名と参照先のクラス名を分けたい場合に使う
:foreign_key 参照先のテーブルの外部キーのカラム名
:primary_key 参照元(自分)のテーブルの外部キーのカラム名

🗽 補足:dependentオプション

dependentオプションは、モデルの親レコードを削除するときに子のレコードを削除するかどうかを決めるオプションです。

class Category < ActiveRecord::Base
has_many :products, dependent: :option
end

オプションの種類は次のとおりです。

オプション 説明
:destroy 親レコードと一緒に子レコードを削除。子レコードのコールバックも実行
:delete_all 親レコードと一緒に子レコードを削除。SQL直接実行なのでコールバックなし
:nullify 子レコードの外部キーをNULL更新する
:restrict_with_exception 子レコードがある場合はActiveRecord::DeleteRestrictionErrorを発生
:restrict_with_error 子レコードがある場合は削除が失敗し、親レコードにエラー情報を付与

🐰 補足:joinsで結合先のscopeを使う

class Movie < ApplicationRecord
belongs_to :actress
scope :year_2017, -> { where(year: 2017) }
end
class Cast < ApplicationRecord
has_many :movies
end
Cast.joins(:movies).merge(Movie.year_2017)
#=> SELECT "casts".* FROM "casts" INNER JOIN "movies" ON "movies"."actress_id" = "casts"."id" WHERE "movies"."year" = $1 [["year", 2017]]

🐞 補足:内部結合で結合先の情報を取得する

class Movie < ApplicationRecord
belongs_to :actress
scope :year_2017, -> { where(year: 2017) }
end
class Cast < ApplicationRecord
has_many :movies
end
Cast.joins(:movies).eager_load(:movies).each do |cast|
puts cast.name
cast.movies.each { |movie| puts " - #{movie.title}" }
end

😀 補足:where句に別テーブルのカラムを設定する

SQLのWHERE句に別のテーブルのカラムを設定するには次のように記述します。

Article.joins(:comments).where("comment.content LIKE '%ABC%''").count

🎳 参照リンク

📚 おすすめの書籍

🖥 サーバについて

このブログでは「Cloud Garage」さんのDev Assist Program(開発者向けインスタンス無償提供制度)でお借りしたサーバで技術検証しています。 Dev Assist Programは、開発者や開発コミュニティ、スタートアップ企業の方が1GBメモリのインスタンス3台を1年間無料で借りれる心強い制度です!(有償でも1,480円/月と格安)