✨
Rails で関連先と紐付いているレコードを取得する
例えば『コメントしているユーザを絞り込む』みたいなことをしたい場合に以下のように .joins
+ .where
で絞り込む、で実現することができます。
class User < ActiveRecord::Base
has_many :comments
end
class Comment < ActiveRecord::Base
belongs_to :user
end
# 必要なデータを生成
User.create(name: "homu").tap {
_1.comments.build(text: "OK").save!
}
User.create(name: "saya").tap {
_1.comments.build(text: "oops").save!
}
User.create(name: "mami")
User.create(name: "mado")
# こんな感じで『comments が存在する user』を絞り込むことができる
User.joins(:comments).where.not(comments: { id: nil }).tap {
puts _1.to_sql
# => SELECT "users".*
# FROM "users"
# INNER JOIN "comments"
# ON "comments"."user_id" = "users"."id"
# WHERE "comments"."id" IS NOT NULL
pp _1.pluck(:name
# => ["homu", "saya"]
}
また Rails 6.0 以降では where.associated
が利用できます。
# User.joins(:comments).where.not(comments: { id: nil }) と同等
User.where.associated(:comments).tap {
puts _1.to_sql
# => SELECT "users".*
# FROM "users"
# INNER JOIN "comments"
# ON "comments"."user_id" = "users"."id"
# WHERE "comments"."id" IS NOT NULL
pp _1.pluck(:name)
# => ["homu", "saya"]
}
注意
関連先が複数ある場合はその数分だけ返って来ます。
# 1User が複数の comments のレコードと紐付いている場合
User.create(name: "homu").tap {
_1.comments.build(text: "OK").save!
_1.comments.build(text: "NICE").save!
}
User.create(name: "saya").tap {
_1.comments.build(text: "oops").save!
}
# 同じ User のレコードが複数返ってくる場合がある
pp User.where.associated(:comments).pluck(:id, :name)
# => [[1, "homu"], [1, "homu"], [2, "saya"]]
もし、重複を消したい場合は .distinct
で回避することができます。
pp User.where.associated(:comments).distinct.pluck(:id, :name)
# => [[1, "homu"], [1, "homu"], [2, "saya"]]
Discussion