日々の記録。

プログラミングのメモや感じた事などを記録。

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側で、進捗に合わせて開始日・終了日を設定するようにしました。

今後あるであろう「自動入力した値を修正したい。」という要望も考慮して・・・。