问题
In my app, I have a User
model, with a goal_ytd
method, which performs some calculations.
In a controller, I have a variable @users
that might be User
or an ActiveRecord::Relation
of users
, and I would like to sum all of the @users
's goal_ytd
s.
My first inclination was:
@users.sum(&:goal_ytd)
Which threw a deprecation warning in both cases because using sum
on an ActiveRecord::Relation
is going away in Rails 4.1.
So, I changed the code to:
@users.to_a.sum(&:goal_ytd)
Which then threw a NoMethodError
because, in a certain circumstance, @users
is assigned by @users = User
and User
has no to_a
method.
Assigning @users
using @users = User.all
throws a deprecation warning because Relation#all
is also deprecated.
Is there a way to get all Users
as an array? Is there a better way?
回答1:
On Rails 4.1
If goal_ydt
is a column in the users table:
@users.sum(:goal_ydt)
If goal_ydt
is a method in User
class:
@users.to_a.sum(&:goal_ydt)
回答2:
I like to use a combination of map
and sum
@users.map(&:goal_ydt).sum
回答3:
You should not use enumerable methods here. Use sum
which is defined on ActiveRecord::Relation and takes symbol as parameter. The main difference is that it will perform SUM
query in your database, so it is much faster than pulling all the records form db. Also if any of your record has blank value for given field, enumerable sum
will throw an error, while ActiveRecord's one will not. In short:
@users.sum(:goal_ydt)
EDIT:
However since goal_ydt
is not a field but a method, you have no choice but to loop over the models. The way I usually do this is by using scoped
method:
@users.scoped.sum(&:goal_ydt)
回答4:
The issue here is rooted in a fundamental misunderstanding of the Relation#all
deprecation. While Relation#all
is deprecated, Model#all
is not. Therefore:
@users = User.all
is still perfectly valid, while:
@users = User.where(first_name: "Mike").all
is deprecated.
So the end solution looks like:
@users = User.all
unless current_user.admin?
@users = @users.where(company_id: current_user.company_id)
end
@users.to_a.sum(&:goal_ytd)
A new question would be: How do I sum all the users goals, preferably in one line, without loading them all into memory? I suppose that's for another day.
来源:https://stackoverflow.com/questions/25274845/rails-4-sum-by-model-method