【发布时间】:2015-11-21 22:25:34
【问题描述】:
我正在尝试构建 Rails 中的“贷款”模型。有一个相应的“支付”模型。贷款余额是贷款的原始金额减去所有付款的总和。计算余额很容易,但我试图计算大量贷款的余额,同时避免 N+1 查询,同时使“余额”成为“贷款”模型的属性。
当我调用 Loans 控制器的 index 方法时,我可以运行自定义选择查询,这允许我通过直接 SQL 查询返回“余额”属性。
class LoansController < ApplicationController
def index
@loans = Loan
.joins("LEFT JOIN payments on payments.loan_id = loan.id")
.group("loans.id")
.select("loans.*, loans.amount - SUM(payments.amount) as balance")
end
def index_002
@loans = Loan.includes(:payments)
end
def index_003
@loans = Loan.includes(:payments)
end
end
class Loan < ActiveRecord::Base
has_many :payments
def balance=(value)
# I'd like balance to load automatically in the Loan model.
raise NotImplementedError.new("Balance of a loan cannot be set directly.")
end
def balance_002
# No N+1 query, but iterating through each payment in Ruby
# is grossly inefficient as well
amount - payments.map(:amount).inject(0, :+)
end
def balance_003
# Even with the "includes" in the controller, this is N+1
amount - (payments.sum(:amount) || 0)
end
end
现在我的问题是如何始终使用我的贷款模型来做到这一点。通常 ActiveRecord 使用以下查询加载一个或多个模型:
SELECT * FROM loans
--where clause optional
WHERE id IN (?)
有没有办法覆盖 Loan 模型,以便它加载以下查询:
SELECT
loans.*, loans.amount - SUM(payments.amount) as balance
FROM
loans
LEFT JOIN
payments ON payments.loan_id = loans.id
GROUP BY
loans.id
这种方式“平衡”是模型的一个属性,只需要在一个地方声明,但我们也避免了 N+1 查询的低效率。
【问题讨论】:
标签: ruby-on-rails activerecord orm