woshidan's blog

そんなことよりコードにダイブ。

ActiveRecord::Baseのcreateとsaveの違い

たまたま仕事で気になったので、ActiveRecord::Basecreatesaveの違いについて簡単に調べました。

まとめ

save(!)メソッドはcreate(!)メソッドで利用されている

https://github.com/rails/rails/blob/master/activerecord/lib/active_record/persistence.rb より。

def create(attributes = nil, &block)
  if attributes.is_a?(Array)
    attributes.collect { |attr| create(attr, &block) }
  else
    object = new(attributes, &block)
    object.save
    object
  end
end

(中略)

def create!(attributes = nil, &block)
  if attributes.is_a?(Array)
    attributes.collect { |attr| create!(attr, &block) }
  else
    object = new(attributes, &block)
    object.save!
    object
  end
end

create(!)メソッドはnewメソッドとsave(!)メソッドを行うメソッド

def create!(attributes = nil, &block)
  if attributes.is_a?(Array)
    attributes.collect { |attr| create!(attr, &block) }
  else
    object = new(attributes, &block)
    object.save!
    object
  end
end

save(!)メソッドを利用しているので、レコードの保存に失敗した場合、createメソッドでは属性が代入されたオブジェクトが返り、create!メソッドでは例外が発生する

save!メソッドsaveメソッドのコードを見てみる

# 例外が発生しても捉えられているので、
# createのメソッドのコードは続行するため、createメソッドでは
# 保存に成功していない状態のオブジェクトが返ってくる
def save(*args)
  create_or_update(*args)
rescue ActiveRecord::RecordInvalid
  false
end

# 例外が発生する
def save!(*args)
  create_or_update(*args) || raise(RecordNotSaved.new("Failed to save the record", self))
end

save(!)メソッドの中を追っていくと、new_record?メソッドの返り値によって、INSERT文を呼び出しているメソッドとUPDATE文を呼び出しているメソッドを分けている風に見える

save(!) メソッドの中にあるcreate_or_update(*args)を探してみる。

def create_or_update(*args)
  raise ReadOnlyRecord, "#{self.class} is marked as readonly" if readonly?
  result = new_record? ? _create_record : _update_record(*args)
  result != false
end

なるほど...。