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

🏀 参照リンク

🖥 VULTRおすすめ

VULTR」はVPSサーバのサービスです。日本にリージョンがあり、最安は512MBで2.5ドル/月($0.004/時間)で借りることができます。4GBメモリでも月20ドルです。 最近はVULTRのヘビーユーザーになので、「ここ」から会員登録してもらえるとサービス開発が捗ります!

📚 おすすめの書籍