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

Rails 6.1: whereの関連付けでjoinedテーブルのエイリアス名を参照可能になった(翻訳)

概要

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

訳注
本記事で言及している#40106のマージコミットにはv6.1.1タグが付けられていますが、2020/08/31の時点でmasterにマージされた#40106は実際にはRails 6.1のコミットリストに含まれており、Rails 6.1 Active RecordのChangelogにも記載されているので、タイトルおよび訳文ではRails 6.1と表記しています。

Rails 6.1: whereの関連付けでjoinedテーブルのエイリアス名を参照可能になった(翻訳)

Rails 6.1で、whereの中でモデル名のエイリアスを指定できる機能が追加されました(#40106)。

たとえば以下のようなEmployeeモデルがあるとします。

class Employee < ActiveRecord::Base
  has_many :subordinates, class_name: "Employee", foreign_key: :manager_id
  belongs_to :manager, class_name: "Employee"
end

管理職が上に付いていない社員のmanager_idnilになります。

変更前

上に付く管理職の名前がSamである社員をすべてフェッチするシナリオで考えてみましょう。

この場合、フェッチ方法は2とおりあります。

Employee.find_by(name: "Sam").subordinates
SELECT "employees".* FROM "employees" WHERE "employees"."name" = $1 LIMIT $2  [["name", "Sam"], ["LIMIT", 1]]
SELECT "employees".* FROM "employees" WHERE "employees"."manager_id" = $1 /* loading for inspect */ LIMIT $2  [["manager_id", "e4f2c753-6057-4952-9947-d1a543b2d1c7"], ["LIMIT", 11]]
=> #<ActiveRecord::AssociationRelation [#<Employee id: "3e584548......
Employee.joins(:manager).where(manager: { name: "Sam" })
ActiveRecord::StatementInvalid (PG::UndefinedTable: ERROR:  missing FROM-clause entry for table "manager")
LINE 1: ..._employees"."id" = "employees"."manager_id" WHERE "manager"....

2番目のクエリはmissing FROM-clause entryエラーになります。

ここではmanageremployeesテーブルを参照することを期待していますが、Railsはそのことを認識できませんでした。

変更後

この問題を修正するため、Rails 6.1ではwhereemployeesテーブルへの参照にエイリアス名を使えるようにする修正が加えられました。

Employee.find_by(name: "Sam").subordinates
SELECT "employees".* FROM "employees" WHERE "employees"."name" = $1 LIMIT $2  [["name", "Sam"], ["LIMIT", 1]]
SELECT "employees".* FROM "employees" WHERE "employees"."manager_id" = $1 /* loading for inspect */ LIMIT $2  [["manager_id", "e4f2c753-6057-4952-9947-d1a543b2d1c7"], ["LIMIT", 11]]
=> #<ActiveRecord::AssociationRelation [#<Employee id: "3e584548......

Employee.joins(:manager).where(manager: { name: "Sam" })
SELECT "employees".* FROM "employees" INNER JOIN "employees" manager ON manager."id" = "employees"."manager_id" WHERE "manager"."name" = $1 /* loading for inspect */ LIMIT $2  [["name", "Sam"], ["LIMIT", 11]]
=> #<ActiveRecord::Relation [#<Employee id: "3e584548......

このユースケースは、自己参照的なモデルとエイリアス化されたテーブルで多くの場合有用です。

関連記事

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

Rails 6.1: 関連付けをバックグラウンド削除する「dependent: :destroy_async」(翻訳)


CONTACT

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