解決までに時間がかかったので備忘録として残す。
以下のようなモデルで登録/更新処理をしたときに User モデルの before_validation
が呼ばれない事があって、原因がわからずハマった。
app/models/team.rb
class Team < ApplicationRecord
has_many :users
accepts_nested_attributes_for :users, allow_destroy: true
end
app/models/user.rb
class User < ApplicationRecord
belongs_to :team
attr_writer :first_name, :last_name
before_validation :set_name
private
def set_name
self.name = "#{@last_name} #{@first_name}"
end
end
ちなみにコントローラーの実装は以下のような感じ。
app/controllers/teams_controller.rb
class TeamsController < ApplicationController
# ...
def create
@team = Team.new(team_params)
return render(:new) if @team.invalid?
@team.save!
end
def update
@team = Team.find(params[:id])
@team.attributes = team_params
return render(:edit) if @team.invalid?
@team.save!
end
private
def team_params
params
.require(:team)
.permit(:name, :logo, users_attributes: [:id, :_destroy, :first_name, :last_name, :gender, :birthday])
end
end
結論としては、Virtual Attributes の変更はモデルの changed?
で検知ができないため、Virtual Attributes のみ更新した時に changed? == false
となってバリデーションが実行されないことが原因。
以下のように validates_associated
を親モデルに追加すると指定した子モデルで常にバリデーションが実行される。
app/models/team.rb
class Team < ApplicationRecord
has_many :users
accepts_nested_attributes_for :users, allow_destroy: true
# 追加
validates_associated :users
end