在相关模型中更改属性后运行before_save

时间:2014-07-24 23:03:18

标签: ruby-on-rails relation before-save

我今天遇到了一些麻烦,无法在盒子外面思考解决这个问题。

基本上我有一个名为Airplane的模型,它有很多付款。每笔付款可分为多个分期付款。 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。但它仅在飞机领域发生变化时才有效。

如果付款和分期付款字段都有更改,我该如何运行此类?

我想检查每次更改分期付款或付款本身时付款是否完成。

谢谢!

2 个答案:

答案 0 :(得分:2)

不是在Airplane模型上定义after save回调,而是在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

这种方法的好处是付款和分期付款中的逻辑是相同的,因此您可以将其提取到模块中:

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

答案 1 :(得分:0)

您可以尝试在belongs_to关联上设置:autosave。如果可行的话,那就是一个聪明的解决方案:)。

更好的选择可能是创建一个after_save挂钩,要求飞机自行检查。

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

这样可以更清楚地了解发生了什么以及为什么。但另一方肯定会更聪明。