13
11

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

has_many な accepts_nested_attributes_for のバリデーションエラーに index 追加するオプション

Last updated at Posted at 2016-03-11

最近 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

13
11
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
13
11

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?