0

サンプル Rails 3.2.8 アプリ (Ruby 1.9.3 上) には、次の簡単なセットアップがあります。

class Account < ActiveRecord::Base
  has_many :line_items

  def subtotal
    line_items.sum(&:price)
  end
end

class Line_Item < ActiveRecord::Base
  belongs_to :product

  def price
    product.price * time
  end
end


account = Account.new

account.line_items.build do |item|
  item.years = 4
  item.product = Product.last
end

account.subtotal
#=> TypeError: nil can't be coerced into BigDecimal

上記のように、subtotalメソッドは変換エラーで失敗します。でsubtotal返される型を確認し、line_items.classを取得しましArrayた。の定義を次のいずれかに更新するsubtotalと、メソッドは機能します。

line_items.to_a.sum(&:price)
#=> #<BigDecimal:7ff4d34ca7c8,'0.0',9(36)>

line_items.map(&:price).sum
#=> #<BigDecimal:7ff4d3373b40,'0.0',9(36)>

line_items.sum(&:price)の最初の定義が失敗するのはなぜですか?

4

1 に答える 1

0

これは Rails のバグのようです (少なくとも 3.2.8 では)。

Rails 3.2.8 では、has_many関連付けは動的に定義されcollection.sumます。これはA Guide to Active Record Associations またはActiveRecord::Associations::ClassMethods API docsには反映されていませんが; APIの「コレクションの関連付け (1 対多 / 多対多)」チャートの下に簡単にリストされていますが、再度参照されることはありません。

コードによると、常に SQL を使用してデータベースにアクセスしようとしますsum。ただし、この特定のインスタンスでは、モデルは保存されません。したがって、データベースには何もありません。

したがって、それを少し明確にするために:

account.line_items.sum(&:price)      # Uses Collection#sum, goes to database
account.line_items.map.sum(&:price)  # Uses Enumerable#map then Enumerable#sum

これはissue #7928として記録されています。問題#5215に関連して表示されます:「新しいオブジェクトのhas_manyアソシエーションのファインダーとスコープは、null外部キーに対してデータベースを誤ってクエリします」 .

于 2012-10-15T14:49:18.923 に答える