rails modelの属性設定メソッドをオーバーライドしてみる
モデルのある属性を変更した際に、別の属性も併せて変更したかったので、モデルの属性を設定するメソッドを定義してみました。
例
やりたいこと。
Todoクラスに、0〜100の範囲の進捗状況という属性があって、0からの変化時に開始日を設定、 100に変化したときに終了日を設定したい。
class Todo < ActiveRecordBase attr_accessible :progress, :begin_date_time, :end_date_time #進捗を設定する. def progress=(val) now = DateTime.now if val > 0 and self.begin_date_time.blank? self.begin_date_time = now end if val == 100 and self.end_date_time.blank? self.end_date_time = now end #実際の属性 progressを設定する. write_attribute(:progress, val) end
属性設定(セッター)のメソッド定義
def progress=(val)
上記メソッドを定義することで、progressの設定処理を記述し、元々のprogress=をオーバーライドします。(オーバーライドという言葉は適切ではないかもしれません。)
write_attribute(:progress, val)
上記メソッドで、実際に属性progressの値を変更しています。
問題点
todoの編集画面でprogressのみ変更した場合、controller.updateの標準の動作では、 begin_date_time/end_date_timeの変更はDBに反映されません。
# todos_controller.rb def update @todo = Todo.find(params[:id]) respond_to do |format| # 画面で変更された属性(paramsで設定されている属性)のみ、updateが適用される。 # # 画面で進捗のみ更新した場合、メモリ上のtodoでは、progressとbegin_date_time #とend_date_timeの属性が更新されますが、DBへは進捗の変更しか反映されません。 if @todo.update_attributes(params[:todo]) format.html { redirect_to parent_or_root_url(@todo), notice: 'Todo was successfully updated.' } format.json { head :no_content } else format.html { render action: "edit" } format.json { render json: @todo.errors, status: :unprocessable_entity } end end end
対処
結局model側での開始日・終了日の設定はあきらめて、 view側で、進捗に合わせて開始日・終了日を設定するようにしました。
今後あるであろう「自動入力した値を修正したい。」という要望も考慮して・・・。