【问题标题】:How to validate an attribute with "validates :attribute" referring to the value of a parent object attribute?如何使用引用父对象属性的值的“validates:attribute”来验证属性?
【发布时间】:2011-07-29 15:11:52
【问题描述】:

O'Reilly 的“Head first Rails”(2009 年编)的这个练习中,有 2 个相关对象

"Flight" 对象[我使用 annotate gem 来显示每个属性]:

# == Schema Information
#
# Table name: flights
#
#  id                :integer         not null, primary key
#  departure         :datetime
#  arrival           :datetime
#  destination       :string(255)
#  baggage_allowance :decimal(, )
#  capacity          :integer
#  created_at        :datetime
#  updated_at        :datetime
#

class Flight < ActiveRecord::Base
  has_many :seats
end

蚂蚁“座位”对象:

# == Schema Information
#
# Table name: seats
#
#  id         :integer         not null, primary key
#  flight_id  :integer
#  name       :string(255)
#  baggage    :decimal(, )
#  created_at :datetime
#  updated_at :datetime
#

class Seat < ActiveRecord::Base
  belongs_to :flight
end

正如您可能猜到的那样,seat.baggage 值应始终小于或等于 seat.flight.baggage_allowance。

所以我写了这个验证器,效果很好:

class Seat < ActiveRecord::Base

  belongs_to :flight

  def validate
      if baggage > flight.baggage_allowance
      errors.add_to_base("Your have to much baggage for this flight!")
    end 
  end  
end

然后我尝试用这个更漂亮的重构它:

      validates :baggage, :numericality => { :less_than_or_equal_to => flight.baggage_allowance }, :presence => true 

但它会在 SeatsController 中导致 NameError

undefined local variable or method `flight' for #<Class:0x68ac3d8>"

比我还尝试使用“self.flight.baggage_allowance”:

validates :baggage, :numericality => { :less_than_or_equal_to => self.flight.baggage_allowance }, :presence => true

但它会抛出 NoMethodError 异常:

undefined method `flight' for #<Class:0x67e9b40>

有没有办法让更漂亮的验证器工作?

进行此类验证的最佳做法是什么?

谢谢。

---编辑---

正如 Maurício Linhares 之后所建议的那样,定义一个 :bagging_allowance 符号是可以解决问题的。 我想更好地了解真正需要自定义验证的地方。 那么这个呢,是否可以将 this 转换为“验证”方法?为什么?

class Seat < ActiveRecord::Base
.
.
.
def validate
  if flight.capacity <= flight.seats.size
    errors.add_to_base("The flight is fully booked, no more seats available!")
  end
end

再次感谢您。

【问题讨论】:

  • 在验证器中尝试匿名函数::less_than_or_equal_to =&gt; Proc.new { |flight| flight.baggage_allowance }
  • 我写了:validates :baggage, :numericality =&gt; { :less_than_or_equal_to =&gt; Proc.new { |flight| flight.baggage_allowance }}, :presence =&gt; true 它抛出:NoMethodError in SeatsController#create undefined method 'baggage_allowance' for #&lt;Seat:0x6a8be38&gt; 无论如何谢谢!

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


【解决方案1】:

你可以这样做:

class Seat < ActiveRecord::Base

  validates :baggage, :numericality => { :less_than_or_equal_to => :baggage_allowance }, :presence => true 

  belongs_to :flight

  def baggage_allowance
    flight.baggage_allowance
  end  
end

编辑

你不能这样做:

class Seat < ActiveRecord::Base
        validates :baggage, :numericality => { :less_than_or_equal_to => flight.baggage_allowance }, :presence => true 
end

因为 validates 方法是在类级别调用的,所以没有可用的 flight 变量,因为它是一个实例变量。当你用 :baggage_allowance 配置它时,你告诉 Rails 调用 Seat 实例上的 :baggage_allowance 方法以便能够访问该值。

【讨论】:

  • 谢谢,它有效!所以似乎在处理“验证”时父模型属性没有初始化。有具体原因吗?你知道我可以在哪里了解更多吗?
  • 您的代码有效并解决了我的问题!我只想了解更多关于“验证”方法的信息,以了解何时无法避免自定义验证(不利于代码的清晰性)。换句话说,我想知道为什么不能在validates 中调用像“flight.baggage_allowance”这样的父方法。我编辑了第一篇文章以更好地解释我的担忧。再次感谢您。
  • 好了,添加到答案中。
猜你喜欢
  • 2010-11-11
  • 1970-01-01
  • 1970-01-01
  • 2018-12-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-07-15
  • 1970-01-01
相关资源
最近更新 更多