😕
Rails accepts_nested_attributes_forは変更の無いレコードをバリデーションしないようだ。
ネステッドアットリビュートを使ったフォームの実装していました。
変更が無い子レコードにはバリデーションしておらず驚きました。
# frozen_string_literal: true
require "bundler/inline"
gemfile(true) do
source "https://rubygems.org"
git_source(:github) { |repo| "https://github.com/#{repo}.git" }
gem "activerecord", "~> 7.0.8"
gem "sqlite3", "~> 1.4"
gem "enumerize"
gem 'pry', '~> 0.14.2'
end
require "active_record"
require "minitest/autorun"
require "logger"
require "pry"
require "enumerize"
ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:")
ActiveRecord::Base.logger = Logger.new(STDOUT)
ActiveRecord::Schema.define do
create_table :posts, force: true do |t|
t.string :status
end
create_table :comments, force: true do |t|
t.integer :post_id
t.string :name
end
end
class Post < ActiveRecord::Base
extend Enumerize
enumerize :status, in: %i(draft publish), scope: true, predicates: { prefix: true }
has_many :comments
accepts_nested_attributes_for :comments, allow_destroy: true
validates :status, presence: true
validates :comments, length: { minimum: 1 }, unless: :status_draft?
end
class Comment < ActiveRecord::Base
belongs_to :post
validates :name, presence: true, if: -> { post_id? && !post.status_draft? }
end
class BugTest < Minitest::Test
def test_association_stuff
post = Post.create!(status: :draft, comments_attributes: [{ name: nil }, { name: nil }])
assert_equal 2, post.comments.count
assert_equal true, post.comments.first.valid?
assert_equal true, post.comments.second.valid?
post.update!(status: :publish, comments_attributes: [{ id: 1, name: '太郎' }])
assert_equal true, post.comments.first.valid?
assert_equal false, post.comments.second.valid?
end
end
◆ 調べたメモ
callerで呼び出しているメソッドを遡りました。
activemodel-7.2.2.1/lib/active_model/validations/presence.rb:7
activerecord-7.2.2.1/lib/active_record/validations/presence.rb:10
activemodel-7.2.2.1/lib/active_model/validator.rb:155
activemodel-7.2.2.1/lib/active_model/validator.rb:151
activemodel-7.2.2.1/lib/active_model/validator.rb:151に、
以下のようにbinding.pryを置いて渡ってくるレコードを確認しました。
binding.pry if record.class.table_name == "comments" && self.kind == :presence && self.attributes == [:name]
変更のないレコードが渡ってきていない == バリデーションされていないよう。
Discussion