Выполнить before_save после изменения атрибута в связанной модели

Сегодня у меня возникли некоторые проблемы, и я не могу мыслить нестандартно, чтобы исправить это.

По сути, у меня есть модель под названием Airplane, в которой has_many Payments. Каждый платеж можно разделить на несколько частей. Ok!

Вот информация:

Model Airplane
- has_many payments
- before_save :checks_if_everything_has_been_paid

Model Payment
- belongs_to airplane
- has_many installments

Model Installment
- belongs_to payment

Итак, что я хочу сделать, так это то, что когда сумма взносов будет равна или больше стоимости билета на самолет, тогда Airplane.paid будет истинным. Я делаю это с помощью функции before_save "checks_if_everything_has_been_paid". Но это работает только при наличии изменений в полях Airplane.

Как я могу запустить этот класс, когда есть изменения в полях «Платеж» и «Рассрочка»?

Я хочу проверить, завершается ли платеж каждый раз, когда изменяется платеж или сам платеж.

Благодарю вас!


person Guido    schedule 24.07.2014    source источник


Ответы (2)


Вместо определения обратного вызова after save для модели Airplane определите обратный вызов after_add для ассоциации payments.

class Airplane < ActiveRecord::Base
  has_many :payments, after_add: :checks_if_everything_has_been_paid

  def checks_if_everything_has_been_paid
    # work some magic
  end
end

Обновление: я думаю, что следующий подход может быть лучшим, если я правильно понимаю вашу модель данных. Если платеж или рассрочка сохранены, это заставит самолет проверить полную оплату:

class Airplane < ActiveRecord::Base
  has_many :payments
  has_many :installments, through: :payments

  def check_for_full_payment
    # work some magic
  end
end

class Payment < ActiveRecord::Base
  belongs_to :airplane
  has_many :installments

  after_save :trigger_full_payment_check

  def trigger_payments_check
    airplane.check_for_full_payment
  end
end

class Installment < ActiveRecord::Base
  belongs_to :payment

  delegate :airplane, to: :payment

  after_save :trigger_full_payment_check

  def trigger_payments_check
    airplane.check_for_full_payment
  end
end

Преимущество этого подхода в том, что логика в Payment и в рассрочку идентична, поэтому вы можете извлечь ее в модуль:

module TriggerFullPaymentCheck
  def self.included(base)
    base.after_save :trigger_full_payment_check
  end

  def trigger_payments_check
    airplane.check_for_full_payment
  end
end

class Airplane < ActiveRecord::Base
  has_many :payments
  has_many :installments, through: :payments

  def check_for_full_payment
    # work some magic
  end
end

class Payment < ActiveRecord::Base
  include TriggerFullPaymentCheck

  belongs_to :airplane
  has_many :installments
end

class Installment < ActiveRecord::Base
  include TriggerFullPaymentCheck

  belongs_to :payment
  delegate :airplane, to: :payment
end
person infused    schedule 25.07.2014
comment
Спасибо @infused! Да, я думаю, это отличная идея... Могу ли я сделать что-то вроде after_add -> self.payment.airplane.save, и он все равно запустит метод на модели самолета? Я думаю, что я просто делаю это более запутанным, чем должно быть. хе-хе - person Guido; 25.07.2014
comment
Могу ли я сделать after_update: для ассоциации платежей? - person Guido; 25.07.2014
comment
@Guido, я обновил свой ответ тем, что считаю лучшим решением - person infused; 25.07.2014
comment
Это сработало! Пришлось написать многословную работу, немного магии, а в остальном все нормально, метод срабатывает, если у меня есть изменения в рассрочке, платеже или самолете. - person Guido; 28.07.2014

Вы можете попробовать установить :autosave для своих belongs_to ассоциаций. Если это работает, это умное решение :).

Лучшим вариантом, вероятно, является создание хука after_save, который просит Airplane проверить себя.

class Airplane
  has_many :payments

  def check_for_paid
    # use payments(true) to force the association to reload
    if payments(true).all?(&:paid?)
      paid = true
      save
    end
  end
end

class Payment
  # associations

  def paid?
    installments(true).all?(&:paid?)
  end
end

class Installment
  after_save :check_for_paid

  def check_for_paid
    payment.airplane.check_for_paid
  end
end

Таким образом, становится более понятно, что происходит и почему. Хотя другой определенно был бы умнее.

person James Mason    schedule 25.07.2014