中村的雑記

技術に関する記事を書いていきます。iOSエンジニア->Railsエンジニア。

モデルが1対多の時にhas_manyではなくhas_oneを使って実装する方法

はじめに

has_oneに条件をつけて1対多を実装できることを知らなかったのでまとめておく。
タイトルでは1対多と書いたが、1個ずつ条件文を書くので、さすがに1対100とかだとこのやり方は現実的でない。

実装

取引を表すDealモデルと 場所を表すPlaceモデルがあって、 一つのDealモデルにつきPlaceは出発地Aと取引場所Bの二種類があるとする。

このときDealモデルでhas_manyを使い1対多の関係で実装することもできるが、下記のように実装することもできる。

has_one :place_A, -> { where(sort: 0) }, class_name: :Place,
                                 dependent: :destroy, inverse_of: :deal
has_one :place_B, -> { where(sort: 1) }, class_name: :Place,
                                 dependent: :destroy, inverse_of: :deal

ちなみにPlaceモデルでは下記のようにsortを定義している。

enum sort: {
  place_A: 0,
  place_B: 1
}

ちなみにinverse_ofに関しては下記の記事が非常に分かりやすかった。

qiita.com

has_many, has_one, belongs_toの時はinverse_ofを書く必要がないが、has_one + 条件分のときは書く必要があるようだ。