はじめに
学習を進めていく中、頻繁に登場するようになってきたsave
とsave!
。この!
の有り無しだけで意味がだいぶ変わってくるなんて…。
アウトプットとしてそれぞれの違いと、簡単な例を挙げてみます。
saveメソッド
- Active Record モデルのインスタンスをデータベースに保存します
- 保存が成功した場合、true を返しますが、保存に失敗した場合は false を返します
- 保存に失敗した場合、エラーが発生することなく単に false を返すだけです
# Example with save
user = User.new(name: "John")
if user.save
puts "User saved successfully!"
else
puts "Failed to save user."
end
save!メソッド
- save メソッドと同様にインスタンスをデータベースに保存しますが、保存に失敗した場合には例外 ActiveRecord::RecordInvalid を発生させます
- つまり、save! メソッドは保存が成功するか、もしくは例外が発生するまで実行がブロックされます
- バリデーションに失敗する可能性がある場合や、データベース障害などの非常に稀なケースに対して、save!を使用することが推奨されます
# Example with save! and exception handling
# 通常の条件分岐でsave!を使用する場合例外処理を行う begin...rescue ブロックで囲む必要がある
begin
if user.save!
puts "User saved successfully!"
else
puts "Failed to save user."
end
rescue ActiveRecord::RecordInvalid => e
puts "Failed to save user: #{e.message}"
end
# Example with save!
begin
user.save!
puts "User saved successfully!"
rescue ActiveRecord::RecordInvalid => e
puts "Failed to save user: #{e.message}"
end
似たような仕組み:create メソッド
- save メソッドと同様にインスタンスを保存しますが、新しいレコードを作成し保存します
- 保存に失敗した場合は、false を返します
# Example with create
user = User.create(name: "Alice")
if user.persisted?
puts "User created successfully!"
else
puts "Failed to create user."
end
トランザクションとの組み合わせ技
※トランザクション内で複数の save や save! を行い、それらが全て成功した場合にのみコミット(確定)、途中でどれか一つでも失敗した場合には全てをロールバック(取り消し)する、という使い方
save メソッドとトランザクションの例
ActiveRecord::Base.transaction do
user = User.new(name: "Alice")
if user.save
address = Address.new(street: "123 Main St", city: "Anytown")
if address.save
puts "User and address saved successfully!"
else
raise ActiveRecord::Rollback, "Failed to save address."
end
else
raise ActiveRecord::Rollback, "Failed to save user."
end
end
- ユーザーが保存されなかった場合には、そのユーザーに関連付けられた他のデータも同時にロールバックされるため、データベース内で不整合が生じることを防ぐことができる
- このコードはアリスという名前で新たなユーザーを作っている。もしアリスがsaveされたら新しいアドレスを登録できる。そして新しいアドレスも無事登録出来たら成功。そうでなかったらアドレス失敗。前段階のユーザーの登録がちゃんと登録できなかったらユーザー登録失敗
- まとめると、トランザクション開始>ユーザー保存>アドレス保存>アドレス保存失敗>ユーザー保存失敗
save! メソッドとトランザクションの例
begin
ActiveRecord::Base.transaction do
user = User.new(name: "Bob")
user.save!
address = Address.new(street: "456 Elm St", city: "Othertown")
address.save!
puts "User and address saved successfully!"
end
rescue ActiveRecord::RecordInvalid => e
puts "Failed to save user or address: #{e.message}"
end
- ボブで登録してsaveする。アドレスを登録してsaveする。そしてユーザーとアドレス登録成功の文字。どちらかの登録でエラーが起きたらユーザーまたはアドレスの登録失敗という文字が発生
- つまり、ユーザー保存>アドレス保存>成功時メッセージ>保存失敗時の処理
さいごに
save
とsave!
のちがいを紹介しました。銀行なんかの引き落とし処理や重要なパスワードの処理など、厳しい基準を設けたいならトランザクションやsave!を使用したほうが安全なのかなと感じました。
少しでも参考になれば幸いです。