【Rails】DBに保存する前のデータを削除する方法は`delete`
オブジェクトをnewしたけど、条件に応じて内容が空になることがわかった。
この状態でsaveしちゃうとvalidationに引っかかるしオブジェクトを削除したい。
が、DB保存前なのでActiveRecordで削除する感じで
delete
はできないしどうすれば...
そんな疑問に今日は応えていきます!
今日のお題はこちら
- DB保存前のオブジェクトを削除する方法
今日親に紐づく子のデータを複数同時に保存する処理を作っていたんですが、 formが空の場合カラムにnilがせっとされたまま送られてくるのでvalidationに引っかかるし不要なデータなので削除した。
だけど、DBに保存前だしこれどうやって削除するんだ?ってなって調べてみて解決したので、それを共有できればと思います。
モデルの構成
親モデル: character(キャラクター) 子モデル: deathblow(必殺技)
ER図
# character.rb class Character < ApplicationRecord extend Enumerize has_many :deathblows accepts_nested_attributes_for :deathblows, allow_destroy: true enumerize :sex, in: [:male, :female] validates :name, presence: true validates :sex, presence: true end
# deathblow.rb class Deathblow < ApplicationRecord belongs_to :character validates :name, presence: true validates :acquisition_level, presence: true end
DB保存前のオブジェクトを削除する方法
結論としては、親モデル.子モデル達.delete(削除する子モデル)
でokです!
つまり
character.deathblows(deathblow)
では実際に画面のキャプチャー/コード/ログを見ながら解説して行きます。
必殺技登録: edit
マッシュのレベルが6になりましたので、魔列車で大活躍の「オーラキャノン」を習得させてあげましょう!w
# characters_controller.rb class CharactersController < ApplicationController def edit @character = Character.find params[:id] @character.build_default_deathblows end def update @character = Character.find params[:id] @character.update(character_params) end private def character_params params.require(:character).permit(:name, :sex, deathblows_attributes: [:name, :acquisition_level]) end end
# character.rb class Character < ApplicationRecord def build_default_deathblows (self.deathblows.count..7).each do self.deathblows.build end end end
# edit.haml %h1 キャラクターの登録 = simple_form_for(@character, url: characters_path) do |f| = f.input :name = f.input :sex, collections: Character.sex.values.delete_if {|v| v == 'male'} = f.fields_for :deathblows do |cf| = cf.input :name = cf.input :acquisition_level = f.submit
必殺技登録: update
マッシュは、まだレベル6なので「オーラキャノン」までしか覚えられません。
だけど、「むげんとうぶ」まで覚える事を考慮してform自体は最大の8つまで準備しているせいで 無駄なデータが送られきてしまいます。
ここで、今日の本題。こいつらも一緒にsaveしちゃうとvalidationに引っかかり弾かれちゃいます。
なので、データを削除します!
def update @character = Character.find params[:id] @character.attributes = character_params @character.build_default_deathblows.each do |deathblow| @character.build_default_deathblows.delete(deathblow) unless deathblow.valid? end @character.update(character_params) end
attributes
でデータを親モデルにセット- 子モデルをeachで1つ1つデータをチェックする
valid
してvalidationが問題ないかチェック- エラーならdeleteする
こんな感じです。
まとめ
本来ならば、動的にformが追加できるようにするべきです。 削除しまっているので、validationで引っかかった場合は、editに戻りますがbuildする処理を通らないので formが消えっぱなしになりますので。
では最後に、もう一度おさらいです。
# 親モデル.子モデル達.delete(子モデル) ↓ @character.build_default_deathblows.delete(deathblow)
ps.
やってて気づきましたが、deleteする処理はmodelでbefore_save
使えばコントローラー側は2行で済むかもですね。
def update @character = Character.find params[:id] @character.update(character_params) end