Rails 7: ActiveRecord::Relation#structurally_compatible?が追加(翻訳)
Railsの優れている点のひとつは、SQLコードをまったく書かずにデータベースオブジェクトやクエリを簡単に扱えることです。これが可能なのは、Railsのコアライブラリの中で間違いなく最高峰であるActive Recordライブラリのおかげです。
Active Recordクエリインターフェイスを使えば、find
やgroup
やjoins
といったさまざまなクエリ操作を実行できます。
また、where
やnot
やor
操作を用いてリレーションをチェインすることも可能です。ただしor
やand
操作については、チェインする2つのリレーションに構造上の互換性を持たせておく必要があります。
2つのリレーションに構造上の互換性を持たせるには、両者が同じモデルを対象とし、かつwhere
(group
が定義されていない場合)またはhaving
(group
が存在する場合)だけが異なっていなければなりません。
従来は、2つのリレーションが構造上の互換性を持っているかどうかを簡単に確かめる方法がありませんでした。リレーションの値をイテレーションするか、or
やand
で発生するArgumentErrorをキャッチする必要がありました。
しかし、クエリでor
やand
を実行する前に2つのリレーションが構造上の互換性を持っているかどうかを簡単にチェックできるActiveRecord::Relation#structurally_compatible?
がRails 7に追加されました(#41841)。
たとえばPost
モデルに :content
、:user_id
、status
という属性があり、Comment
モデルに:post_id
、:user_id
、:content
という属性があるとしましょう。Post
はhas_many :comments
を持ち、Comment
はbelongs_to :post
を持っています。
構造上の互換性を持つリレーションが2つあるとします。
relation_1 = Post.where(status: 'active')
relation_2 = Post.where(id: current_user.id)
また、構造上の互換性を持たないリレーションも2つあるとします。
relation_3 = Post.where(status: 'active')
relation_4 = Post.joins(:comments).where(comments: { user_id: current_user.id})
変更前
これらのリレーション間でor
クエリを実行しなければならなくなった場合、ArgumentError
をキャッチします。
relations = [[relation_1, relation_2], [relation_3, relation4]]
relations.each do |relation|
left = relation.first
right = relation.last
begin
left.or(right)
rescue ArgumentError
# 構造上の互換性を持たないリレーションが失敗したらArgumentErrorをrescueする
end
end
変更後
Rails 7では、リレーション同士が構造上の互換性を持つかどうかをクエリ実行前にチェックできます。
relations = [[relation_1, relation_2], [relation_3, relation4]]
relations.each do |relation|
left = relation.first
right = relation.
if left.structurally_compatible?(right)
left.or(right)
end
end
これは、例外をキャッチせずにリレーションを手軽にクエリできる素晴らしい改善です。このような小さなヘルパー機能が豊富にあるからこそ、Active Recordが優秀なライブラリとなり、Railsフレームワークが開発者にとってより使いやすくなっています。
概要
原著者の許諾を得て翻訳・公開いたします。
参考: 週刊Railsウォッチ20210830『
ActiveRecord::Relation#structurally_compatible?
が追加』