6
7

More than 5 years have passed since last update.

ActiveRecord で "最新の子" を has_one で表現する

Last updated at Posted at 2018-10-11

たとえば、複数のバージョンを持つブログの記事を下記のようなモデルで表現しているとする。

class Entry < ApplicationRecord
  has_many :versions

  def latest_version
    versions.order(number: :desc).first
  end
end

class Version < ApplicationRecord
  belongs_to :entry
  # 新しいバージョンほど number の値が大きいものとする
  validates :number, uniqueness: true, numericality: {greater_than: 0, only_integer: true}
end

このとき最新のバージョンは Entry.find(1).latest_version で得られるが、単なるメソッドなので includes などを使うことができない。

has_one では絞り込みの条件を lambda で渡す事ができるので下記のように書けばよい。

class Entry < ApplicationRecord
  has_many :versions
  has_one :latest_version, -> {
    # 同じ entry_id で number がこれより大きいレコードが存在しない、という条件
    where(
      <<~SQL
        NOT EXISTS (
          SELECT 1 FROM versions AS v
            WHERE
              versions.number < v.number AND
              versions.entry_id = v.entry_id
        )
      SQL
    )
  }, class_name: "Version"
end

これで Entry.includes(:latest_version).find(1).latest_version のように書ける。

6
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
7