コード日進月歩

しんくうの技術的な小話、メモ、つれづれ、など

has_manyでorderを設定したものを無視したいときはreorderを使う

設定しているときに無視したいという特殊なケースの対策。基本は打ち消しをしないことを前提に書いたほうがよいが書かざるを得ないときにどうするかのためのメモ。

環境

$ bin/rails -v
Rails 7.1.2

前提

has_manyなどの子の関連に関して、デフォルトの並び順を設定することができる。詳しくは以下の記事を参照のこと。

RailsのActiveRecordのhas_manyにはorderで順番の指定できる - コード日進月歩

例えば以下のようなModelがあったとする

# == Schema Information
#
# Table name: soccer_teams
#
#  id         :bigint           not null, primary key
#  name       :string(255)
#  created_at :datetime         not null
#  updated_at :datetime         not null
#
class SoccerTeam < ApplicationRecord
  has_many :soccer_member, -> { order(:name) }
end
# == Schema Information
#
# Table name: soccer_members
#
#  id             :bigint           not null, primary key
#  name           :string(255)
#  soccer_team_id :bigint           not null
#  created_at     :datetime         not null
#  updated_at     :datetime         not null
#
class SoccerMember < ApplicationRecord
  belongs_to :soccer_team
end

普通に SoccerTeam がhas_manyで持っている SoccerMember を取得しようとすると以下のようになる。

SoccerTeam.first.soccer_member.to_sql
# "SELECT `soccer_members`.* FROM `soccer_members` WHERE `soccer_members`.`soccer_team_id` = 5 ORDER BY `soccer_members`.`name` ASC"

例えばこれをid順にしたいと思ったときに下記のようにしても、nameのorderが優先されてしまう

SoccerTeam.first.soccer_member.order(:id).to_sql
# "SELECT `soccer_members`.* FROM `soccer_members` WHERE `soccer_members`.`soccer_team_id` = 5 ORDER BY `soccer_members`.`name` ASC, `soccer_members`.`id` ASC"

これを回避するためには reorder を利用する。

SoccerTeam.first.soccer_member.reorder(:id).to_sql
"SELECT `soccer_members`.* FROM `soccer_members` WHERE `soccer_members`.`soccer_team_id` = 5 ORDER BY `soccer_members`.`id` ASC"

関連リンク