Help us understand the problem. What is going on with this article?

【Rails】データを削除する際、関連レコードがある場合にアラートを出す

More than 1 year has passed since last update.

概要

  • dependent: :restrict_with_error をつける
  • redirect_toのnoticeにerrors.messages[:base]を表示する image.png

目的

has_many関連レコードが設定されている時、関連レコードが設定されている場合は削除しないようにする

手順

現在運営している学会検索のイベントサイトdokodeを運営しています。その中で、イベントページ(Event)は下記の関係を持っています
Eventにはイベントの情報、EventVenueにはイベントの場所情報が入っています。
今回は、Eventのデータ削除時にEventVenueにデータがあった場合は削除せずアラートを出すようにします。

alt

手順は以下の通りです。

  1. Eventのhas_manyのdependentオプションに:restrict_with_errorをつける
  2. controllerのdestroyアクションを修正する
  3. 日本語化する

has_manyのdependentオプションに:restrict_with_errorをつける

親がEventで子がEventVenueです。Eventモデルのhas_manyを以下のように修正します。

app/models/event.rb
class Event < ApplicationRecord
  has_many :event_venues, dependent: :restrict_with_error

restrict_with_errorについてRAILS GUIDESの説明をみると以下の通りです。

4.2.2.4 :dependent
オーナーオブジェクトがdestroyされた時に、それに関連付けられたオブジェクトをどうするかを制御します。
省略
:restrict_with_errorを指定すると、関連付けられたオブジェクトがある場合にエラーがオーナーに追加されます。

今回の事例では、いかが対応します。

  • 関連付けられたオブジェクト=EventVenue。
  • オーナー=Event

エラーというのは、event.errorsのことです。validationで使うやつです。restrict_with_error箇所のソースを見ると、 :base にエラーメッセージが追加されることがわかります。

rails/activerecord/lib/active_record/associations/has_many_association.rb
owner.errors.add(:base, :'restrict_dependent_destroy.has_many', record: record)

event.errors.messages[:base]でエラーメッセージを呼び出すことができます。

controllerのdestroyアクションを修正する

上記で重要なことは話終えたので、あとは、実際に実装した例を示します。
削除したときと言うことで、呼び出されるのは コントローラの destory メソッドですので、こちらを呼び出すモジュールを下記の通実装しました。

app/controllers/concerns/custom_destroy.rb
module CustomDestroy
  extend ActiveSupport::Concern

  included do
    def destroy
      model = controller_name.classify.constantize # コントローラ名からモデルを作る
      record = model.find(params[:id]) # モデルからレコードを呼び出す。
      record.destroy # 削除命令
      if record.errors.present? # 削除が失敗した場合
        redirect_to url_for(controller: controller_name, action: :index), notice: "#{record.errors.messages[:base].join('。')}" and return # 削除が失敗した場合は、notificationに関連レコードのモデル名を表示する
      else # 削除が成功した場合
        redirect_to url_for(controller: controller_name, action: :index), notice: "#{record.model_name.human}は削除されました" and return # 削除が成功した場合は、notificationにレコード自身のモデル名を表示する
      end
    end
  end
end

上記のモジュールのdestroyを導入したいコントローラでincludeします。
今回はEventのコントローラーに導入します。なお、destoryアクションが既に導入されている場合は消して下さい。

app/controllers/events_controller.rb
class EventsController < ApplicationController
  include CustomDestroy # これで 上記のモジュールを読みこまれるので、このコントローラーにdestoryメソッドが導入されます。

表示は下記のようなヘルパーメソッドを追加して下さい。

views/xxxx.html.erb
<%= content_tag :div, flash['notice'], class: "alert-dismissible alert alert-info" %>

これで削除(destory)を呼び出すと下記のような表示が行われます。

image.png

日本語化する

上記までで、動きは完成です。ただ、関連モデル名が英語のままですので、日本語化します。
日本語化するときは、下記の通り追加して下さい。

config/locales/ja.yml
ja:
  activerecord:
    attributes:
      event:
        event_venues: イベント場所 # ←これ

結果

dokodeの管理者用のイベント情報管理ページに導入した例です。

Image from Gyazo

以上です。

junara
10年間医療の研究していたが、Webサービス作りたくて、エンジニアへ。 Rails, Javascritptのエンジニアとして働きながら、生命科学学会検索サービスを作りました。 https://www.dokode.work
bldt
"Your growth, Improve the world" をビジョンに掲げ、自社事業やシステム開発サービスを提供しています
https://bldt.jp
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away