如何改进Category模型的Scope以返回含交易总额的自定义数据?
改进Category Scope以包含交易总额
嘿,我来帮你调整这个scope,让它既能按交易总额排序,又能把总额作为属性返回给每个Category实例。
首先,你可以通过select方法把计算出的总额作为别名字段选出来,同时把这段逻辑封装成可复用的scope会更方便。直接修改后的scope代码如下:
# 在Category模型中定义这个scope scope :with_total_debit_amount, ->(time_range = 1.month.ago) { joins(:transactions) .where('transactions.created_at >= ?', time_range) .group('categories.id') .select('categories.*, SUM(transactions.debit_amount_cents) AS all_amount') .order('all_amount DESC') }
为什么这么改?
select('categories.*, SUM(...) AS all_amount')会把分类的所有字段,加上计算出的交易debit总额(别名为all_amount)一起查询出来,这样每个Category对象就会自带all_amount属性,你直接调用就行。- 我把时间范围做成了可选参数,默认是1个月前,这样你调用的时候可以灵活调整,比如想查3个月内的就用
Category.with_total_debit_amount(3.months.ago)。
使用方式
调用这个scope后,就能直接访问每个分类的总额了:
@categories = Category.with_total_debit_amount # 遍历输出示例 @categories.each do |cat| puts "id: #{cat.id}, name: #{cat.name}, all_amount: #{cat.all_amount}" end
额外补充:保留无交易的分类
如果某个分类在指定时间范围内没有交易,上面的内连接(joins)会自动排除它。要是你想保留这些分类,并且让它们的all_amount显示为0,可以改成左外连接,同时用COALESCE处理NULL值:
scope :with_total_debit_amount, ->(time_range = 1.month.ago) { left_outer_joins(:transactions) .where('transactions.created_at >= ? OR transactions.id IS NULL', time_range) .group('categories.id') .select('categories.*, SUM(COALESCE(transactions.debit_amount_cents, 0)) AS all_amount') .order('all_amount DESC') }
这里的left_outer_joins会保留所有分类,COALESCE把没有交易时的NULL总额转换成0,where条件里的transactions.id IS NULL确保无交易的分类不会被时间条件过滤掉。
内容的提问来源于stack exchange,提问作者SsPay
相关产品推荐
相关产品推荐

