LoginSignup
0
0

More than 1 year has passed since last update.

accepts_nested_attributes_forで子要素を保存する時にfirst使ってはいけない

Posted at

accepts_nested_attributes_forにおいて、最初のデータの保存がうまくいかないケースがあった。

前提

# app/models/estimate.rb

class Estimate < ActiveRecord::Base
  has_many :estimate_items
  accepts_nested_attributes_for :estimate_items
end

❌ ダメなコード

# rails console

$> est = Estimate.find(1)
$> puts est.estimate_items.first.name
=> "test1"
$> est.estimate_items.first.name = "hoge"
$> est.save!
$> puts est.estimate_items.first.name
=> "test1"  # 「hoge」が保存されてない!

🙆‍♂️ OKなコード

# rails console

$> est = Estimate.find(1)
$> puts est.estimate_items.first.name
=> "test1"
$> est.estimate_items[0].name = "hoge"
$> est.save!
$> puts est.estimate_items.first.name
=> "hoge"  # 「hoge」が保存されている!

理由

firstがついているとSQLにlimitがついて has_many :estimate_items の全量がロードされないから。

# rails console

est.estimate_items.first
#=> SELECT * from estimate_items where estimate_id = 1 limit 1

est.estimate_items[0]
#=> SELECT * from estimate_items where estimate_id = 1
ちなみに、うっかりデバッグでputsとかするとfirstでも保存されるようになる。
# rails console

$> est = Estimate.find(1)
$> puts est.estimate_items  # ←デバッグ用のつもりが処理に影響を及ぼしている
#=> SELECT * from estimate_items where estimate_id = 1
$> puts est.estimate_items.first.name
=> "test1"
$> est.estimate_items.first.name = "hoge"
$> est.save!
$> puts est.estimate_items.first.name
=> "hoge"  # 「hoge」が保存されている!

 このロードの仕組みはデバッグがしづらいので結構ハマる。ちゃんと理解してないと本番環境でもRails.loggerが実際には処理に影響を及ぼすこともあるので要注意。

対策

accept_nested_attributesの時はfirst使わず[0]でデータ更新しよう。

0
0
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
0
0