我试图为每个用户检索一个名为"contribution_amount"的关联列,但我得到未定义的方法错误,我不知道为什么。
控制器有:@payments = Payment.where(:contribution_date => Date.today).pluck(:user_id)
@users = User.where(:id => @payments).find_each do |user|
user.payments.contribution_amount
end
模型有:
class User < ActiveRecord::Base
has_many :payments
end
class Payment < ActiveRecord::Base
belongs_to :user
end
控制台的准确错误是
`undefined method `contribution_amount' for #<ActiveRecord::Associations::CollectionProxy::ActiveRecord_Associations_CollectionProxy_Payment:0x007fb89b6b2c08>`
user.payments
是作用域;也就是说,它表示Payment
记录的集合。contribution_amount
方法仅适用于单个Payment
记录。你可以说user.payments.first.contribution_amount
,但我不确定这是你的目标。
你在试着计算贡献金额吗?在这种情况下,您需要使用一个方法来聚合记录集合:user.payments.sum(:contribution_amount)
。
暂时偏离主题,通常最好将范围界定方法推到模型中。例如:
class User < ActiveRecord::Base
def self.with_payment_contribution_after(date)
joins(:payments).merge(Payment.with_contribution_after(date))
end
def self.with_contribution_amount
joins(:payments).group("users.id")
.select("users.*, sum(payments.contribution_amount) as contribution_amount")
end
end
class Payment < ActiveRecord::Base
def self.with_contribution_after(date)
where(:contribution_date => date)
end
end
# In your controller
@users = User.with_payment_contribution_after(Date.today)
.with_contribution_amount
# In a view somewhere
@users.first.contribution_amount
以这种方式构建代码的优点是:
你的作用域不再锁定在控制器方法中,所以你可以很容易地在其他地方重用它们。
你的控制器方法可以变得更简单和更具声明性。也就是说,它可以表达它想要什么信息,而不是如何获取该信息。
将作用域分解为更小的部分意味着我们的代码可以更好地分解,并且已经分解的代码可以重新组合。
通过模型单元测试比通过控制器测试更容易测试作用域。