Active Record でのポリモーフィック結合
9424 ワード
多形関連
Active Record では、polymorphic associations を使用して、モデルが 1 つの関連付けで複数のモデルに属することを許可できます.
booking
が accommodation
または office
に属する例を次に示します.class Booking < ApplicationRecord
belongs_to :bookable, polymorphic: true
end
class Accommodation < ApplicationRecord
has_many :bookings, as: :bookable
end
class Office < ApplicationRecord
has_many :bookings, as: :bookable
end
この
bookable
関連付けが機能するためには、bookings
テーブルが bookable_id
列と bookable_type
列を保持する必要があることに注意してください. Rails の公式ドキュメントには、ポリモーフィック アソシエーションの実装方法が説明されています ( here ).ポリモーフィック アソシエーションへの参加に関する問題
ポリモーフィック アソシエーションで
bookable
を直接結合しようとすると、エラーが発生します.Booking.joins(:bookable)
ActiveRecord::EagerLoadPolymorphicError (Cannot eagerly load the polymorphic association :bookable)
これは、一般的な
bookable
関連付けの下に多くのテーブルが存在する可能性があるため、Active Record が結合するテーブルを認識していないためです.この問題を解決する方法の 1 つは、外部キーと型列を使用して、結合する予約可能なテーブルを明示的に示す SQL 文字列を渡すことです.
Bookings.joins("INNER JOIN accommodations ON accommodations.id = bookings.bookable_id AND bookings.bookable_type = 'Accommodation'")
ただし、クエリから
offices
テーブルを除外していることに注意してください.これも結合したい場合は、同様の結合ステートメントを追加する必要があります.Bookings
.joins("INNER JOIN accommodations ON accommodations.id = bookings.bookable_id AND bookings.bookable_type = 'Accommodation'")
.joins("INNER JOIN offices ON offices.id = offices.bookable_id AND bookings.bookable_type = 'Office'")
bookable
アソシエーションをさらに追加し、その上で追加のクエリを実行する必要がある場合、混乱が生じる可能性があることは容易に想像できます.これらの冗長な SQL 文字列を渡す代わりに、次のようなことができれば役に立ちます.
Bookings.joins(:accommodation, :office)
すべての予約可能なものに参加したい場合でも、
bookable
個の関連付けをすべて渡す必要がありますが、代わりに関連付けをシンボルとして渡すことで実現できます.ここで実行しようとすると、まだエラーが発生します.
ActiveRecord::ConfigurationError: Can't join 'Booking' to association named 'accommodation'; perhaps you misspelled it?
予約モデルは
bookable
エンティティのみを認識しているため、Active Record は宿泊施設とオフィスを関連付けとして個別に認識しません.これらの関連付けを Booking モデルに個別に追加できたらどうでしょうか?
範囲指定された関連付けの使用
bookable_type
および foreign_key
で範囲を指定することにより、特定の関連付けを定義できます.class Booking < ApplicationRecord
belongs_to :bookable, polymorphic: true
belongs_to :accommodation, -> { where(bookings: { bookable_type: 'Accommodation' }) }, foreign_key: 'accommodation_id'
belongs_to :office, -> { where(bookings: { bookable_type: 'Office' }) }, foreign_key: 'office_id'
end
ここで、前のクエリを再度実行すると、タイプが「宿泊施設」と「オフィス」のすべての予約が取得されます.内部の SQL は、カスタム結合で以前に記述したものとまったく同じです.クエリで
.to_sql
メソッドを呼び出すことで確認できます.Bookings.joins(:accommodation, :office).to_sql
=> "SELECT \"bookings\".* FROM \"bookings\"
INNER JOIN \"accommodations\" ON \"accomodations\".\"id\" = \"bookings\".\"bookable_id\" AND \"bookings\".\"bookable_type\" = 'Accommodation'
INNER JOIN \"offices\" ON \"offices\".\"id\" = \"bookings\".\"bookable_id\" AND \"bookings\".\"bookable_type\" = 'Office'
ハッピークエリ!
Active Record への参加の詳細:
Reference
この問題について(Active Record でのポリモーフィック結合), 我々は、より多くの情報をここで見つけました https://dev.to/anakbns/polymorphic-joins-in-active-record-2hojテキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol