Is there a shorter way to do the following (
@user.employees.map { |e| { id: e.id, name: e.name } }
# => [{ id: 1, name: 'Pete' }, { id: 2, name: 'Fred' }]
User
has_many
employees. Both classes inherit from ActiveRecord::Base
.
Two things I don't like about the above
Is there a better way?
UPDATE:
see @jamesharker's solution: from ActiveRecord >= 4, pluck
accepts multiple arguments:
@user.employees.pluck(:id, :name)
PREVIOUS ANSWER:
for a single column in rails >= 3.2, you can do :
@user.employees.pluck(:name)
... but as you have to pluck two attributes, you can do :
@user.employees.select([:id, :name]).map {|e| {id: e.id, name: e.name} }
# or map &:attributes, maybe
if you really need lower-level operation, just look at the source of #pluck , that uses select_all
在ActiveRecord> = 4中, pluck接受多个参数,因此该示例将变为:
@user.employees.pluck(:id, :name)
如果你被卡住了Rails 3,您可以添加这个 .pluck_all
扩展: http://meltingice.net/2013/06/11/pluck-multiple-columns-rails/
Add this monkey patch which provides the multi columns pluck functionality in Rails 3.
# config/initializers/pluck_all.rb
if Rails.version[0] == '3'
ActiveRecord::Relation.class_eval do
def pluck(*args)
args.map! do |column_name|
if column_name.is_a?(Symbol) && column_names.include?(column_name.to_s)
"#{connection.quote_table_name(table_name)}.#{connection.quote_column_name(column_name)}"
else
column_name.to_s
end
end
relation = clone
relation.select_values = args
klass.connection.select_all(relation.arel).map! do |attributes|
initialized_attributes = klass.initialize_attributes(attributes)
attributes.map do |key, attr|
klass.type_cast_attribute(key, initialized_attributes)
end
end
end
end
end
Rename the method from pluck
to pluck_all
if you dont want to override the original pluck
functionality
In terms of making a rails 3 method that behaves the same as the Rails 4 pluck with multiple columns. This outputs a similar array (rather than a hashed key value collection). This should save a bit of pain if you ever come to upgrade and want to clean up the code.
module ActiveRecord
class Relation
def pluck_all(*args)
args.map! do |column_name|
if column_name.is_a?(Symbol) && column_names.include?(column_name.to_s)
"#{connection.quote_table_name(table_name)}.#{connection.quote_column_name(column_name)}"
else
column_name.to_s
end
end
relation = clone
relation.select_values = args
klass.connection.select_all(relation.arel).map! do |attributes|
initialized_attributes = klass.initialize_attributes(attributes)
attributes.map do |key, attribute|
klass.type_cast_attribute(key, initialized_attributes)
end
end
end
end
end
Standing on the shoulders of giants and all
The pluck_all
method worked well until I'm going to upgrade from Rails 3.2 to Rails 4.
Here is a gem pluck_all to solve this, making pluck_all
method support not only in Rails 3 but in Rails 4 and Rails 5. Hope this will help those who are going to upgrade rails version.
Another option is to:
@user.employees.select(:id, :name).as_json
#=> [{"id" => 1, "name" => "Pete"}, {"id" => 2, "name" => "Fred"}]
I can imagine that you'd rather have symbolized keys. If that's the case use the #symbolize_keys
method.
@user.employees.select(:id, :name).as_json.map(&:symbolize_keys)
#=> [{id: 1, name: "Pete"}, {id: 2, name: "Fred"}]
See: http://api.rubyonrails.org/classes/ActiveModel/Serializers/JSON.html#method-i-as_json
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.