Railsで変更前の(DBに保存されている)値を利用してvaldiateする

Webアプリでvalidationを書いていると、DBに保存されている値が必要になる場合があります。Railsでは、DBに保存されている値は、#{attr_name}_in_database、保存されている値から変更があったかは、will_save_change_to_#{attr_name}?というメソッドが用意されています。#{attr_name}の部分はModelの属性(カラム名)で置き換えます。

変更前の値を利用してvaldiateする

前回に引き続き、会議室予約アプリを例にして、具体的な使い方をみてみます。

会議室(meeting_room)は複数の会議予約(meeting)をもちます。会議予約は、開始時刻(start_time)と終了時刻(end_time)をもちます。

f:id:yucatio:20180809214216p:plain

★前回の記事

yucatio.hatenablog.com

仕様

  • 会議予約(前回実装済み)
    • subjectは32文字以下で必須項目
    • start_timeは必須項目
    • end_timeは必須項目
    • end_timeはstart_timeより後であること
    • 新規登録時、start_timeは現在時刻より後であること
    • start_timeが変更された時、start_timeは現在時刻より後であること

これに、

  • 会議予約
    • 保存済みのstart_timeが現在より前の場合は、start_timeを変更できない
    • 保存済みのstart_timeが現在より前の場合は、end_timeを変更できない

この2つのvalidationを追加します。

コード

class Meeting < ApplicationRecord
  # 略

  validate :start_time_cannot_be_changed
  validate :end_time_cannot_be_changed

  private
  # 略

  def after_meeting_start?
    start_time.present? && !new_record? && start_time_in_database <= Time.zone.now
  end

  def start_time_cannot_be_changed
    if after_meeting_start?
      if will_save_change_to_start_time?
        errors.add(:start_time, 'は変更できません。会議開始時刻を過ぎています')
      end
    end
  end

  def end_time_cannot_be_changed
    if after_meeting_start?
      if will_save_change_to_end_time?
        errors.add(:end_time, 'は変更できません。会議開始時刻を過ぎています')
      end
    end
  end
end

after_meeting_start?メソッド内の start_time_in_databaseでは、DBに保存されている(つまり更新前の)start_timeの値を取得できます。 start_time.present? && !new_record? && start_time_in_database <= Time.zone.now は、start_timeがnilでなく、新規レコードでなく(≒更新で)、DBに保存されているstart_timeが、現在時刻より前である、という意味です。

start_time_cannot_be_changedメソッド内でwill_save_change_to_start_time?を使用して、値が変更された時にエラーを出すようになっています。end_time_cannot_be_changedメソッドも同様です。

実行結果

更新時

※実行時は2018年8月10日

開始時間が過去で登録されていて、日時が変更無しのとき(subjectのみ変更)は更新できる↓

f:id:yucatio:20180811154046p:plain

開始時間が過去の場合、他の日時へは変更できない↓

f:id:yucatio:20180811222525p:plain

開始時間が未来の場合、別の未来の日時には変更できる↓

f:id:yucatio:20180811161128p:plain

環境

続く

こちらもどうぞ

yucatio.hatenablog.com

参考

Rails4とRails5ではDBの値をとるメソッドや変更されたかを取得するメソッドの名前が変更されています。rail5の方がわかりやすいメソッド名ですね。#{attr_name}_in_databasewill_save_change_to_#{attr_name}? 以外のメソッドもこちらにまとまっています。

qiita.com

[商品価格に関しましては、リンクが作成された時点と現時点で情報が変更されている場合がございます。]

Ruby on Rails 5アプリケーションプログラミング [ 山田祥寛 ]
価格:3888円(税込、送料無料) (2018/8/9時点)