如何根据父对象的状态验证嵌套模型对象?

时间:2011-05-12 08:02:12

标签: ruby-on-rails ruby ruby-on-rails-3 validation activerecord

我正在用铁轨写一个向导表格;例如一个模型对象的多个输入页面。

我的方法的基础是Ryan Bates的Multistep形式railscast中描述的那些:http://railscasts.com/episodes/217-multistep-forms(如果有人想知道下面某些代码背后的原因)。

这里受审查的对象是“参与者”,其中有一个“地址”

我的问题是我只想在用户尝试通过地址输入屏幕时验证嵌套对象(地址)。目前通过名为“current_step”

的Participant模型上的属性进行跟踪

所以我有一个参与者:

class Participant < ActiveRecord::Base
  has_one :address
  accepts_nested_attributes_for :address
  validates_presence_of :first_name, :last_name, :if => self.current_step == "name"
  ...
  def steps = %w[ name address confirm ] # the steps the wizard will follow
end

和地址:

class Address < ActiveRecord::Base
  belongs_to :participant
  validates_presence_of :address1, :state, :suburb, :postcode #, :if => participant.current_step == "address"
end

这种方法的原理是在控制器(未示出)上为向导的每个步骤调用“创建”操作,并且它仅在处理每个步骤时验证模型的子集。

目前,当我完成第一个屏幕(“名称”)并尝试进入地址步骤时,地址验证将被触发,我将被发送回“名称”屏幕,其中包含空白地址详细信息的验证错误

所以我在这里尝试了很多方法,其中最后一部分是上面显示的地址验证的注释条件 - 我发现这不起作用,因为我只构建参与者 - &gt;地址对象,但不是保存它们。因此@participant.address获取地址对象,但@participant.address.participant为空,因为地址还没有一个participant_id外键来查找它的父级。

我挣扎的原因似乎是包含了超级方便的accepts_nested_attributes_for方法。我希望使用validates_associated进行验证,但我发现accepts_nested_attributes_for标记很好地传播表单参数以创建嵌套模型对象,但也确保participant#valid?方法调用到所有情况下的地址验证。

所以我的困境是如何最好地使用participant#valid?方法根据参与者中的current_step参数来验证部分完整的模型?

编辑 - 更新以删除额外信息,并提炼到核心问题

3 个答案:

答案 0 :(得分:18)

Address型号上添加虚拟属性:

class Address < ActiveRecord::Base
  belongs_to :participant
  attr_accessor :skip_validation
  validates_presence_of :address1, :state, :suburb, :postcode, 
                           :unless => :skip_validation 
end

设置current_step时,在地址对象上设置虚拟属性。

class Participant < ActiveRecord::Base
  has_one :address
  accepts_nested_attributes_for :address
  attr_accessor :current_step
  validates_presence_of :first_name, :last_name, 
                         :if => lambda {|r| r.current_step == "name"}


  def current_step=(value)
    unless (value == "address")
      address.skip_validation = true
    end    
    @current_step = value 
  end    
end

答案 1 :(得分:0)

在致电participant.valid?

之前,只需在控制器中指定它即可
@participant.address.participant = @participant

答案 2 :(得分:0)

虽然ActiveRecord“验证”方法非常方便,但没有什么可以阻止你“滚动你自己的”,也许使用before_save钩子。您可以使用validates_presence_of挂钩替换Address上的before_save验证程序,该挂钩仅在特定条件下进行验证。然后accepts_nested_attributes_for可能不会看到它。