Ruby on Rails のhas_many 関連付けのフィルタテクニック4種(翻訳)

こんにちは、hachi8833です。 今回は、Duck Type Labの記事「4 ways to filter has_many associations」を、原著者の許諾を得て掲載いたします。 今回は初の試みとして、Google翻訳にかけた元サイトも眺めながら翻訳してみました。比較してみると面白いかもしれません。 元記事について 元サイト: Duck Type Lab 元記事: 4 ways to filter has_many associations 著者: Sid Krishnan 本記事は原著者の許諾を得て翻訳・掲載しております。翻訳は例によって原文との1対1対応ではなく、多くの最適化を行ったりリンクを日本語サイトに変更したりしていますのでご了承ください。 TechRachoでは以前にも[翻訳] そのパッチをRailsに当てるべきかを考えるでSidの記事を翻訳でご紹介いたしましたので、合わせてご覧ください。 著者のSid Krishnanは無料のニュースレターを主催していますので、ご興味のある方は元記事の最下部またはニュースレターのサンプルで「Subscribe」をクリックしてニュースレターを購読いただけます。 翻訳: has_many 関連付けのフィルタテクニック4種 has_many関連付けが設定されたモデルが1つあるとしましょう。そのモデルと関連付けレコードの両方に条件をつける形で、関連付けられたモデルのコレクションを取得したい、なんてことはよくあります。たぶん皆さまもきっとこの種の作業をやってみたことがおありかと思いますが、思いつきの作業で無駄な苦労をするよりも(失礼、他意はありません)、もう少しまともな戦略を立ててから取り組めばよかったと感じたのではないでしょうか。 has_many関連付けのフィルタリングはそうした面倒な問題のひとつになることが多く、さまざまな資料を読み込んでSQLやActiveRecordの知識をブラッシュアップしておく必要があります。 たとえば、システムにUserモデルとProjectモデルがあり、指定の日付範囲で作成されたprojectsに関連付けられているusersのコレクションを取り出すクエリを作成したいとしましょう。 クエリでどんな結果が欲しいかによって、次の4つのアプローチが考えられます。 1. 最も単純な方法 ActiveRecordのjoinsメソッドとwhereメソッドを組み合わせて次のようなコードを書きます。 User.joins(:projects).where(projects: { zipcode: 30332 }) この方法は、レコードを1つ(またはそれ以上)の属性でフィルタする場合には適切です。このコードで、zipcodeが30332のプロジェクトに関連するUserレコードのコレクションを取り出せますね。 ただし注意をおひとつ。このjoinメソッドでは実際にはINNER JOINを行っているので、結果に重複が含まれる可能性があります。重複を回避する方法については2.をご覧ください。 2. mergeメソッドを使う方法 再利用するためのスコープがモデルに定義されていることがあります。こうしたスコープをフィルタで使うには、ActiveRecordのmergeメソッドを知っておくと便利です。ActiveRecordのドキュメントによると、mergeは呼び出し元のActiveRecord::Relationとmergeに引数として渡したActiveRecord::Relationのintersect(重複のない積集合)を配列として返します。 たとえば、最近10日以内に作成されたすべてのprojectsを返すopened_recentlyというスコープがProjectモデルに定義されているとします。この場合、以下のように書くことができます。 User.joins(:projects).merge(Project.opened_recently) このコードは、「最近10日以内に作成されたプロジェクトがある」という条件をすべて満たすUserオブジェクトのリストを返します。 1.と同様、has_many関連付けでjoinを使うと、実際にはINNER … Continue reading Ruby on Rails のhas_many 関連付けのフィルタテクニック4種(翻訳)