Как проверить атрибут с помощью validates :attribute, ссылающегося на значение атрибута родительского объекта?

В этом упражнении из книги О'Рейли "Head first Rails" (изд. 2009 г.) есть 2 связанных объекта.

Объект Flight [я использовал аннотацию, чтобы показать каждый атрибут]:

# == 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

Ant объект "Место":

# == 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 

Но это вызывает NameError в SeatsController:

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>

Есть ли способ заставить работать более красивый валидатор?

Как лучше всего проводить такую ​​проверку?

Спасибо.

---РЕДАКТИРОВАТЬ---

Как впоследствии любезно предложил Маурисио Линьярес, проблему можно решить, определив символ :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

Еще раз спасибо.


person Darme    schedule 29.07.2011    source источник
comment
Попробуйте анонимную функцию в вашем валидаторе: :less_than_or_equal_to => Proc.new { |flight| flight.baggage_allowance }   -  person arnep    schedule 29.07.2011
comment
Я написал: validates :baggage, :numericality => { :less_than_or_equal_to => Proc.new { |flight| flight.baggage_allowance }}, :presence => true Выдает: NoMethodError in SeatsController#create undefined method 'baggage_allowance' for #<Seat:0x6a8be38> Все равно спасибо!   -  person Darme    schedule 29.07.2011


Ответы (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 вызывать метод :baggage_allowance для экземпляра Seat, чтобы получить доступ к значению.

person Maurício Linhares    schedule 29.07.2011
comment
Спасибо, это работает! Таким образом, кажется, что атрибуты родительской модели не инициализируются при обработке проверки. Есть ли конкретная причина? Вы знаете, где я могу узнать больше об этом, пожалуйста? - person Darme; 29.07.2011
comment
Ваш код работает и решил мою проблему! Я только хотел бы узнать больше о методе проверки, чтобы понять, когда нельзя избежать пользовательской проверки (в ущерб ясности кода). Другими словами, я хотел бы знать, ПОЧЕМУ невозможно вызвать родительский метод, такой как flight.baggage_allowance в validates. Я отредактировал первый пост, чтобы лучше объяснить мою озабоченность. Еще раз спасибо. - person Darme; 29.07.2011
comment
Ну вот, добавил к ответу. - person Maurício Linhares; 29.07.2011