如何制作范围,将输出自定义数据?

How to make scope, that will output custom data?

我有模型 CategoryTransactions

Category has_many transactions, Transaction belongs_to category.

而且我有 Category 的空间:

@relation = Category.all

@relation.joins(:transactions).where('transactions.created_at >= ?', 1.month.ago).
group('categories.id').order('SUM(transactions.debit_amount_cents) DESC')

它显示类别并按 transactions.debit_amount_cents

的总和对它们进行排序

我想显示其所有交易的金额以及每个类别。

喜欢:

id: 1,
name: "Category1",
all_amount: *some value* #like this

如何改进这个范围?

class Category < ApplicationRecord
  # remember that scope is just a widely abused syntactic sugar
  # for writing class methods
  def self.with_recent_transactions
    joins(:transactions)
      .where('transactions.created_at >= ?', 1.month.ago)
      .select(
         'categories.*',
         'SUM(transactions.debit_amount_cents) AS total_amount'
       )
       .order('total_amount DESC')
       .group('categories.id')
      
  end
end

如果您 select 列或聚合并为其指定别名,它将在生成的模型实例中可用。

Category.with_recent_transactions.each do |category|
  puts "#{category.name}: #{category.total_amount}"
end

为了便携性,您可以使用 Arel 而不是 SQL 字符串来编写它,这样可以避免像 table 名称这样的硬编码内容:

class Category < ApplicationRecord
  def self.with_recent_transactions
    t = Transaction.arel_table
    joins(:transactions)
      .where(transactions: { created_at: Float::Infinity..1.month.ago })
      .select(
         arel_table[Arel.star]
         t[:debit_amount_cents].sum.as('total_amount')
       )
       .order(total_amount: :desc) # use .order(t[:debit_amount_cents].sum) on Oracle
       .group(:id) # categories.id on most adapters except TinyTDS
  end
end

Rails 6.1 (backported to 6.0x) 中,您可以使用无开始范围来创建没有 Float::Infinity 的 GTE 条件:

.where(transactions: { created_at: ..1.month.ago })