おぴよの気まぐれ日記

おぴよの気まぐれ日記

岡山やプログラミング、ファッションのこと、子育てや人生、生き方についての備忘録。

【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

f:id:opiyotan:20190723232659p:plain

必殺技登録: update

マッシュは、まだレベル6なので「オーラキャノン」までしか覚えられません。

だけど、「むげんとうぶ」まで覚える事を考慮してform自体は最大の8つまで準備しているせいで 無駄なデータが送られきてしまいます。

f:id:opiyotan:20190723233202p:plain

ここで、今日の本題。こいつらも一緒に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