概要
目的
has_many関連レコードが設定されている時、関連レコードが設定されている場合は削除しないようにする
手順
現在運営している学会検索のイベントサイトdokodeを運営しています。その中で、イベントページ(Event)は下記の関係を持っています
Eventにはイベントの情報、EventVenueにはイベントの場所情報が入っています。
今回は、Eventのデータ削除時にEventVenueにデータがあった場合は削除せずアラートを出すようにします。
手順は以下の通りです。
- Eventのhas_manyのdependentオプションに
:restrict_with_error
をつける - controllerのdestroyアクションを修正する
- 日本語化する
has_manyのdependentオプションに:restrict_with_error
をつける
親がEventで子がEventVenueです。Eventモデルのhas_manyを以下のように修正します。
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
にエラーメッセージが追加されることがわかります。
owner.errors.add(:base, :'restrict_dependent_destroy.has_many', record: record)
event.errors.messages[:base]でエラーメッセージを呼び出すことができます。
controllerのdestroyアクションを修正する
上記で重要なことは話終えたので、あとは、実際に実装した例を示します。
削除したときと言うことで、呼び出されるのは コントローラの destory
メソッドですので、こちらを呼び出すモジュールを下記の通実装しました。
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アクションが既に導入されている場合は消して下さい。
class EventsController < ApplicationController
include CustomDestroy # これで 上記のモジュールを読みこまれるので、このコントローラーにdestoryメソッドが導入されます。
表示は下記のようなヘルパーメソッドを追加して下さい。
<%= content_tag :div, flash['notice'], class: "alert-dismissible alert alert-info" %>
これで削除(destory)を呼び出すと下記のような表示が行われます。
日本語化する
上記までで、動きは完成です。ただ、関連モデル名が英語のままですので、日本語化します。
日本語化するときは、下記の通り追加して下さい。
ja:
activerecord:
attributes:
event:
event_venues: イベント場所 # ←これ
結果
dokodeの管理者用のイベント情報管理ページに導入した例です。
以上です。