在质量赋值初始化之前设置activerecord模型默认值

时间:2014-11-11 12:05:25

标签: ruby-on-rails activerecord callback mass-assignment

我需要在ModelA中将对象从ModelAController#new传递给视图之前设置默认值(来自远程服务)。我已经使用了after_initialize。但是,#create我遇到了问题。如果我使用model_b.create_model_a(some_attributes),则属性在初始化期间传入,然后由after_initialize调用覆盖:

class ModelA < ActiveRecord::Base
  after_initialize :set_defaults, if: :new_record?

  def set_defaults
    self.c = "default"
    #actually a remote call, not something can be set as database default
  end
end

class ModelB < ActiveRecord::Base
  belongs_to :model_a
end

class ModelAController < ApplicationController
  #ModelA is nested under ModelB in routes.rb

  #GET /model_bs/:model_b_id/model_as/new
  def new
    model_b = ModelB.find(params[:model_b_id])
    #no problem
    respond_with model_b.build_model_a
  end

  #POST /model_bs/:model_b_id/model_as
  def create
    model_b = ModelB.find(params[:id])
    #problem:
    respond_with model_b.create_model_a({c: "not default"})
    #at this point the model_a instance still has attribute c set to "default"
  end
  ...
end

我可以分开创建步骤:

model_b = ModelB.find(params[:id])
model_a = model_b.build_model_a #should fire after_initialize
model_a.update_attributes({c: "not default"}) #overwrite default c value

但我觉得这会让ModelA的生命周期成为其他程序员的陷阱。这似乎是将最后两行重构为一个的明显候选者,但这会再次产生这个问题。有更整洁的解决方案吗?

1 个答案:

答案 0 :(得分:1)

进行条件分配:

def set_defaults
  self.c ||= "default"
end

或者,而不是after_initialize hook在属性读取器中设置默认值。这样,您只需在实际需要属性值时设置默认值,这样如果您不需要它,它可以为您节省远程通话:

def c
  super || self.c = 'default'
end