按虚拟属性而不是数据库字段过滤记录?

时间:2012-10-20 17:47:45

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

我在User模型中使用此功能来填充表单中的选择框:

def outstanding_invoices
  invoices.to_a.select { |invoice| invoice.balance < 0 }
end

问题是balance不是数据库字段,而是计算某些东西的方法。

因此,生成选择框需要大量的SQL查询,因为上面的函数会迭代invoice所拥有的每一个user

是否有更快的方法来生成选择框,理想情况下只使用一个SQL查询?

一个简单的替代方法可能是将每个balance的{​​{1}}存储在数据库中,但我不知道这样做是因为我想将数据库保持在最低限度。

感谢您的帮助。

1 个答案:

答案 0 :(得分:2)

根据使用计算货币值的经验,在很多情况下将计算值与用于计算该值的参数一起存储是有意义的 - 这样您可以针对计算值运行查询,但您也有关于如何计算价值的可追溯性。

在您的发票示例中,我建议您在after_save模型中实施Payment回调。例如:

class Payment < ActiveRecord::Base
  belongs_to :invoice

  def after_save
    invoice.update_balance
  end
end

class Invoice
  has_many :payments

  def self.with_open_balance
    where("current_balance > 0")
  end

  def update_balance
    update_attributes(:current_balance => balance)
  end

  def balance
    invoiced_amount - payments.sum(:amount)
  end
end

通过这种方法,您现在可以致电customer.invoices.with_open_balance以获取该客户的所有未结发票。每当保存付款时,付款所属的发票将重新计算其余额并将该计算值存储在同一数据库事务中,以确保一致性。