LoginSignup
5
6

【Rails】実務でのsaveとcreate、create(save)とcreate!(save!)の使い分け

Last updated at Posted at 2023-10-09

こんにちは。プログラミングスクールHappiness Chainでメンターをしているryoです。

今回はスクールでsaveとcreate、create(save)とcreate!(save!)をどのように使い分けるのか、質問があったので、実際に実務でどのように使い分けているかお話しします。

saveとcreateの違いと、エラーハンドリングの方法

saveとcraeteの違いは、以下になります。

  • save

    • newで作成したインスタンスを、DBに保存する
    • 返り値として、true(DB保存成功)orfalse(DB保存失敗)を返す
  • create

    • インスタンス生成 + DB保存を一括で行う
    • 返り値として、DB保存成功・失敗に関わらず、インスタンスを返す(違いとして、IDの有無)
  • 共通点

    • インスタンスのerrorsに、ActiveModel::Errorインスタンスが配列に格納される

上記を考慮すると、エラーハンドリングは以下になります。

  • saveの場合
def create
  @user = User.new(user_params)
  if @user.save
    # 成功の処理
  else
    flash[:alert] = @user.errors.full_messages.join("\n")
    render :new
  end
end

  • createの場合
def create
  @user = User.create(user_params)
  if @user.id.present?
    # 成功の処理
  else
    flash[:alert] = @user.errors.full_messages.join("\n")
    render :new
  end
end

createの場合は、idが存在するかどうかで 判断する必要があります。
個人的には、saveの方が、エラーハンドリングをする際に分かりやすい かなと思います。

create(save)とcreate!(save!)の違いと、エラーハンドリングの方法

実務では、create!をよく使うので、create!の方で説明します。

「!」を付ける場合と付けない場合の違いは、
失敗した場合に、例外処理を発生させるかどうか になります。

上記で記載したcreateの場合を、create!に書き直すと以下になります。

def create
  @user = User.create!(user_params)
  # 成功の処理
rescue => e
  flash[:alert] = e.messages
  render :new
end

ただ上記の場合だと、ちょっと分かりずらいと感じるかもしれないですし、
エラーが考えられる場合って、@user = User.create!(user_params)
の部分のみですので、
1レコードの作成・更新の場合は、「!」を付けないで記載する 方が、どこでエラーが発生するのか明示的で良いかなと思います。

逆に、2レコード以上の作成・更新がある場合は、transactionを考慮する必要があり、
ActiveRecord::Base.transactionで括った処理で、例外処理が起こった場合に、 rollbackするので、
以下のように記載することができます。

def create
  ActiveRecord::Base.transaction do
    @user = User.create!(user_params)
    @profile = @user.profile.create!(profile_params)
  end
  # 成功の処理
rescue => e
  flash[:alert] = e.messages
  render :new
end

もし、上記をcreateで書こうとした場合は、以下のようになります。

def create
  ActiveRecord::Base.transaction do
    @user = User.create(user_params)
    raise @user.errors.full_messages.join("\n")  if user.id.nil?
    @profile = @user.profile.create(profile_params)
    raise @profile.errors.full_messages.join("\n")  if profile.id.nil?
  end
  # 成功の処理
rescue => e
  flash[:alert] = e.messages
  render :new
end

上記だと、成功したかどうかを条件分岐して、raiseを自分で書く必要があります。
ですので、2レコード以上の作成・更新がある場合は、create!(save!)を使う 方がすっきりします。

まとめ

  • 1レコードの作成・更新は、saveを使う
  • 2レコード以上の作成・更新は、create!を使う
5
6
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
5
6