最近 rails5 の activerecord の changelog を眺めてます。
で、「has_many な accepts_nested_attributes_for のバリデーションエラーに index 追加するオプション」の話。
例えば
class User < ActiveRecord::Base
has_many :conditions
accepts_nested_attributes_for :conditions
end
class Condition < ActiveRecord::Base
belongs_to :user
validates :value, presence: true
end
みたいな has_many な関係の nested attributes の時。
rails4
cirb(main):002:0> user = User.find(4)
User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1 [["id", 4]]
=> #<User id: 4, gender: "woman", age: 11, created_at: "2016-02-25 13:11:13", updated_at: "2016-02-25 13:11:13">
irb(main):003:0> params = {user: { age: 12, conditions_attributes: [{value: 'ok'}, {value: ''}, {value: 'bad'}]}}
=> {:user=>{:age=>12, :conditions_attributes=>[{:value=>"ok"}, {:value=>""}, {:value=>"bad"}]}}
irb(main):004:0> user.attributes = params[:user]
=> {:age=>12, :conditions_attributes=>[{:value=>"ok"}, {:value=>""}, {:value=>"bad"}]}
irb(main):005:0> user.valid?
=> false
irb(main):006:0> user.errors
=> #<ActiveModel::Errors:0x007fe0c3ced6b8 @base=#<User id: 4, gender: "woman", age: 12, created_at: "2016-02-25 13:11:13", updated_at: "2016-02-25 13:11:13">, @messages={:"conditions.value"=>["can't be blank"]}>
こんな感じでバリデーションエラーが出た時も nested attributes でエラーになったのはわかるけど、何番目なのか? がわからなくて不親切でした。
実は rails5 でも、そのままだと状況は同じです。
rails5
irb(main):001:0> user = User.find(5)
User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 5], ["LIMIT", 1]]
=> #<User id: 5, gender: "woman", age: 60, created_at: "2016-02-25 13:11:13", updated_at: "2016-02-25 13:11:13">
irb(main):002:0> params = {user: {age: 61, conditions_attributes: [{value: 'good'}, {value: ''}, {value: 'bad'}]}}
=> {:user=>{:age=>61, :conditions_attributes=>[{:value=>"good"}, {:value=>""}, {:value=>"bad"}]}}
irb(main):003:0> user.attributes = params[:user]
=> {:age=>61, :conditions_attributes=>[{:value=>"good"}, {:value=>""}, {:value=>"bad"}]}
irb(main):004:0> user.valid?
User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 5], ["LIMIT", 1]]
User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 5], ["LIMIT", 1]]
User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 5], ["LIMIT", 1]]
=> false
irb(main):005:0> user.errors
=> #<ActiveModel::Errors:0x007f96523c4070 @base=#<User id: 5, gender: "woman", age: 61, created_at: "2016-02-25 13:11:13", updated_at: "2016-02-25 13:11:13">, @messages={:"conditions.value"=>["can't be blank"]}, @details={"conditions.value"=>[{:error=>:blank}]}>
ところが、 has_many の定義で以下のように
class User < ApplicationRecord
has_many :conditions, index_errors: true
accepts_nested_attributes_for :conditions
end
index_errors
オプションを指定すると。
こんなふうに
rails5-index_errors付き
irb(main):001:0> user = User.find(5)
User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 5], ["LIMIT", 1]]
=> #<User id: 5, gender: "woman", age: 60, created_at: "2016-02-25 13:11:13", updated_at: "2016-02-25 13:11:13">
irb(main):002:0> params = {user: {age: 61, conditions_attributes: [{value: 'good'}, {value: ''}, {value: 'bad'}]}}
=> {:user=>{:age=>61, :conditions_attributes=>[{:value=>"good"}, {:value=>""}, {:value=>"bad"}]}}
irb(main):003:0> user.attributes = params[:user]
=> {:age=>61, :conditions_attributes=>[{:value=>"good"}, {:value=>""}, {:value=>"bad"}]}
irb(main):004:0> user.valid?
User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 5], ["LIMIT", 1]]
User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 5], ["LIMIT", 1]]
User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 5], ["LIMIT", 1]]
=> false
irb(main):005:0> user.errors
=> #<ActiveModel::Errors:0x007f9652ba16a8 @base=#<User id: 5, gender: "woman", age: 61, created_at: "2016-02-25 13:11:13", updated_at: "2016-02-25 13:11:13">, @messages={:"conditions[1].value"=>["can't be blank"]}, @details={"conditions[1].value"=>[{:error=>:blank}]}>
messages や details の key が conditions[1].value
になってる!
今までこれができなくて nested attributes 面倒とか思ってたんですが、便利になったかも。
詳細は、該当の差分見るのが多分一番はやいです。
https://github.com/rails/rails/commit/118232aef4094ce0a1dfbb827b672509e7c33542