Rails で関連先と紐付いているレコードを取得する

2024/08/23に公開

例えば『コメントしているユーザを絞り込む』みたいなことをしたい場合に以下のように .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"]]
GitHubで編集を提案

Discussion