update()
はattributesを引数に取れるのでsave()
でも出来るよなーと思ってRails Consoleで確認したら出来るときと出来ないときがあったので確認した。
まずはupdate
の実装がどうなっているかを公式ドキュメントのupdateで確認。
update()
は以下の実装になっている。
# File activerecord/lib/active_record/persistence.rb, line 423
def update(attributes)
# The following transaction covers any possible database side-effects of the
# attributes assignment. For example, setting the IDs of a child collection.
with_transaction_returning_status do
assign_attributes(attributes)
save
end
end
内部的には assign_attributes
してからsave
しているだけ。
ここで疑問としてsave
って引数に更新対象渡せば更新してくれたと思うんだけど何故assign_attributes
しているのか?と疑問に思った。
なのでsave
の実装を確認。
save()
は以下の実装になっており、この create_or_update
で*args
が有効になるのは更新時のみ。
# File activerecord/lib/active_record/persistence.rb, line 274
def save(*args, &block)
create_or_update(*args, &block)
rescue ActiveRecord::RecordInvalid
false
end
# File activerecord/lib/active_record/persistence.rb, line 702
def create_or_update(*args, &block)
_raise_readonly_record_error if readonly?
return false if destroyed?
result = new_record? ? _create_record(&block) : _update_record(*args, &block)
result != false
end
なのでsave()
で新規登録時に引数に name: 'luccafort', message: 'こんにちわこんにちわ'
というのを期待しても保存はされず、更新時にのみ有効になる。
なので当初思い込んでいたsave()
で渡した引数が有効になる、は半分正解だけど半分不正解。
save()
でも値を反映されてほしい場合は↓みたいにする必要があるってことでいいのかな?
User.save() do |u|
u.name = '名無しの権兵衛'
end
追記:
Twitterで「save()
で新規登録するときはcreate
を使っている」という指摘を受けた。
明示的に新規登録と更新は分けたほうがコンテクストが混在せずに良さそうだし、新規登録時の処理もupdate()
と同じように引数を取るのでこちらのほうが勘違いせずに済みそう。
createの実装はこうなっているので明示的にするならこっちのほうが勘違いせずにすむのでこちらのほうが良さそう。
# File activerecord/lib/active_record/base.rb, line 504
def create(attributes = nil, options = {}, &block)
if attributes.is_a?(Array)
attributes.collect { |attr| create(attr, options, &block) }
else
object = new(attributes, options)
yield(object) if block_given?
object.save
object
end
end
この辺の違いをちゃんと認識してなかったので update()
は引数渡すと更新してくれるけど save()
はOKなときとNGなときがあるなーとざっくりした認識をしていたがようやくきちんと理解できた。
似たような勘違いをしている人がいたのでままあることなのか?と思いつつ、めちゃくちゃお世話になるメソッドの割にちゃんとわかってないの怖いなと思いましたまる