Railsのdependent: :restrict_with_error と :restrict_with_exception はなにが違うのか

こんにちは!kossyです!




さて、今回はRailsのdependentオプションに指定できる、
restrict_with_error と restrict_with_exception はなにが違うのかについて、
ブログに残してみたいと思います。




環境
Ruby 2.6.3
Rails 6.0.3
MacOS Catalina




そもそもdependentオプションとは?

まずはドキュメントを読んでみましょう。

Controls what happens to the associated objects when their owner is destroyed:

そのレコードが破棄されたときに、関連付けられたオブジェクトに何が起こるかを制御します。

出典: https://edgeguides.rubyonrails.org/association_basics.html#options-for-has-one-dependent

平たくいうと、レコード削除時に、関連づけられたオブジェクトをどうするかの制御を可能にするオプションですね。

いくつか割り当てられるオプションがありますが、今回はタイトルにもある通り、
restrict_with_errorとrestrict_with_exceptionに焦点を当ててみます。



restrict_with_errorとrestrict_with_exceptionの違い

ドキュメントを見てみます。

:restrict_with_exception causes an exception to be raised if there are any associated records.

関連するレコードがある場合、例外が発生します。

:restrict_with_error causes an error to be added to the owner if there are any associated objects.

関連するオブジェクトがある場合、所有者にエラーが追加されます。

出典: https://apidock.com/rails/ActiveRecord/Associations/ClassMethods/has_many

restrict_with_exceptionの方は、レコードの削除メソッドが呼ばれたとき、
関連するオブジェクトがある場合は削除できずにActiveRecord::DeleteRestrictionError例外が発生します。

restrict_with_errorの方は、レコードの削除メソッドが呼ばれたとき、
関連するオブジェクトがある場合は削除できずにレコードのerrorsにエラーメッセージ が追加されます。


検証コード

Rspecで検証コードを書いてみました。

前提として、Rspecとfactory_botを用いていて、

user has_many :daily_reports, dependent: :restrict_with_exception
user has_many :schedules, dependent: :restrict_with_error

という関連が組まれているとします。

以下、コード全晒しです。

  describe 'association' do
    describe 'daily_reports' do
      context 'dependent: :restrict_with_exception' do
        it 'expect raise ActiveRecord::DeleteRestrictionError' do
          user = create :user
          create :daily_report, user: user

          expect { user.destroy! }.to raise_error(ActiveRecord::DeleteRestrictionError)
          expect(user.errors.full_messages).to eq []
        end
      end
    end

    describe 'schedules' do
      context 'dependent: :restrict_with_error' do
        it 'expect raise ActiveRecord::RecordNotDestroyed' do
          user = create :user
          create :schedule, user: user

          expect { user.destroy! }.to raise_error(ActiveRecord::RecordNotDestroyed)
          expect(user.errors.full_messages.any?).to eq true
        end
      end
    end
  end

restrict_with_exceptionの場合は、ActiveRecord::DeleteRestrictionError例外が発生し、
user.errors.full_messagesの返り値は空になっていることが確認できました。

restrict_with_errorの場合は、ActiveRecord::RecordNotDestroyed例外が発生し、
user.errors.full_messagesにはエラーメッセージが格納されたことが確認できました。



どう使い分けるか

エラーメッセージ をユーザーに見せたい場合は、restrict_with_errorを使って、
そうでない場合はrestrict_with_exceptionを使えばいいように思います。



大いに参考にさせていただいた記事


この場を借りて御礼申し上げます。