Tech Racho エンジニアの「?」を「!」に。
  • Ruby / Rails関連

Rails 7: 関連付けのあるレコードを取れる'associated'クエリメソッドが追加(翻訳)

概要

原著者の許諾を得て翻訳・公開いたします。

日本語タイトルは内容に即したものにしました。以下の関連記事もどうぞ。

追記(2021/09/27): 当初の原文はRails 6.1が対象となっていましたが、Rails 7が対象ではないかとのご指摘をいただき再確認したところ、原文もRails 7に修正されていたため、本記事も修正いたしました。ありがとうございます🙇

Rails 7: 関連付けのあるレコードを取れるassociatedクエリメソッドが追加(翻訳)

Rails 7で、ActiveRecord::Relation#missingに似たassociatedというクエリメソッドが追加されました(#40696)。missingは孤立化したオブジェクトのリストを取れるActiveRecord::Relationを返すので、孤立化したオブジェクトが存在するかどうかをチェックできます。associatedは関連付けが存在するオブジェクトのリストを取れるActiveRecord::Relationを返すので、関連付けのあるオブジェクトが存在するかどうかをチェックできます。

以下の3つのモデルで考えてみましょう。

# app/models/manager.rb
# Manager: 担当管理職
class Manager < ApplicationRecord
  has_many :job_listings
end
# app/models/job_listing.rb
# JobListing: 求職リスト
class JobListing < ApplicationRecord
  has_many :job_applications
  belongs_to :manager
end
# app/models/job_application.rb
# JobApplication: 求職への応募
class JobApplication < ApplicationRecord
  belongs_to :job_listing
end

Rails 7より前

担当管理職を参照できる求職リストをすべて検索するときは、以下のように行っていました。

[1] pry(main)> JobListing.joins(:manager).where.not(managers: {id: nil})
JobListing Load (0.2ms)  SELECT "job_listings".* FROM "job_listings"
INNER JOIN "managers" ON "managers"."id" = "job_listings"."manager_id"
WHERE "managers"."id" IS NOT NULL LIMIT ?  [["LIMIT", 11]]
=> #<ActiveRecord::Relation [#<Manager id: 3, name: "Jane Doe", created_at: "2020-01-20 14:31:16", updated_at: "2020-01-20 14:31:16">]>

Rails 7以後

Rails 7のActiveRecord::QueryMethods::WhereChainクラスにassociatedクエリメソッドが追加されました(#40696)。

上述の例を用いて、担当管理職を参照可能な求職リストをすべて検索してみましょう。

[1] pry(main)> JobListing.where.associated(:manager)
JobListing Load (0.1ms)  SELECT "job_listings".* FROM "job_listings"
INNER JOIN "managers" ON "managers"."id" = "job_listings"."manager_id"
WHERE "managers"."id" IS NOT NULL LIMIT ?  [["LIMIT", 11]]
=> #<ActiveRecord::Relation [#<Manager id: 3, name: "Jane Doe", created_at: "2020-01-20 14:31:16", updated_at: "2020-01-20 14:31:16">]>
[2] pry(main)>

associatedは単なるシンタックスシュガーです。理由は、先の例と同様に存在チェックを行うINNER JOIN句とWHERE句を含むリレーション(ActiveRecord::Relation)を返すからです。

associatedにはリレーション名を複数渡すことも可能です。

たとえば、担当管理職と応募のどちらも参照可能な求職リストを検索するには以下のようにします。

[1] pry(main)> JobListing.where.associated(:manager, :job_applications)
JobListing Load (0.1ms)  SELECT "job_listings".* FROM "job_listings"
INNER JOIN "managers" ON "managers"."id" = "job_listings"."manager_id"
INNER JOIN "job_applications" ON "job_applications"."job_listing_id" = "job_listings"."id"
WHERE "managers"."id" IS NOT NULL AND "job_applications"."id" IS NOT NULL LIMIT ?  [["LIMIT", 11]]
  => #<ActiveRecord::Relation []>
[2] pry(main)>

このコード例では、担当管理職を参照可能な求職リストが存在する場合でも空のリレーション(#<ActiveRecord::Relation []>)が返されました。理由は、この求職リストから参照できる応募が存在しなかったからです。

関連記事

Rails 6.1: 孤立化したレコードのリストを取れる’missing’クエリメソッドが追加(翻訳)

Rails 6.1で form_withのデフォルトが「remoteなし」になった(翻訳)


CONTACT

TechRachoでは、パートナーシップをご検討いただける方からの
ご連絡をお待ちしております。ぜひお気軽にご意見・ご相談ください。