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)をもちます。
★前回の記事
仕様
- 会議予約(前回実装済み)
- 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のみ変更)は更新できる↓
開始時間が過去の場合、他の日時へは変更できない↓
開始時間が未来の場合、別の未来の日時には変更できる↓
環境
続く
こちらもどうぞ
参考
Rails4とRails5ではDBの値をとるメソッドや変更されたかを取得するメソッドの名前が変更されています。rail5の方がわかりやすいメソッド名ですね。#{attr_name}_in_database
と will_save_change_to_#{attr_name}?
以外のメソッドもこちらにまとまっています。