Ярлык для выщипывания двух атрибутов из объекта ActiveRecord?
Существует ли более короткий способ сделать следующее (
@user.employees.map { |e| { id: e.id, name: e.name } }
# => [{ id: 1, name: 'Pete' }, { id: 2, name: 'Fred' }]
User
has_many
сотрудников. Оба класса наследуют от ActiveRecord::Base
.
Две вещи, которые мне не нравятся выше
- Он загружает сотрудников в память перед отображением,
- Это подробный (субъективный, я думаю).
Есть ли лучший способ?
Ответы
Ответ 1
UPDATE:
см. решение @jamesharker: от ActiveRecord >= 4, pluck
принимает несколько аргументов:
@user.employees.pluck(:id, :name)
ПРЕДЫДУЩИЙ ОТВЕТ:
для одного столбца в rails >= 3.2, вы можете сделать:
@user.employees.pluck(:name)
... но поскольку вам нужно вырвать два атрибута, вы можете сделать:
@user.employees.select([:id, :name]).map {|e| {id: e.id, name: e.name} }
# or map &:attributes, maybe
если вам действительно нужна операция более низкого уровня, просто посмотрите источник #pluck, в котором используется select_all
Ответ 2
В ActiveRecord >= 4 pluck принимает несколько аргументов, чтобы этот пример стал:
@user.employees.pluck(:id, :name)
Ответ 3
Если вы застряли с Rails 3, вы можете добавить это .pluck_all
расширение: http://meltingice.net/2013/06/11/pluck-multiple-columns-rails/
Ответ 4
Добавьте этот патч обезьяны, который обеспечивает многопользовательскую функцию сложения в 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
Переименуйте метод из pluck
в pluck_all
, если вы не хотите переопределять исходную функциональность pluck
Ответ 5
С точки зрения создания метода рельсов 3, который ведет себя так же, как Rails 4, вырывается с несколькими столбцами. Это выдает аналогичный массив (а не хешированный набор значений ключа). Это должно сэкономить немного боли, если вы когда-нибудь приступите к обновлению и хотите очистить код.
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
Стоя на плечах гигантов и всех
Ответ 6
Метод pluck_all
работал хорошо, пока я не перейду с Rails 3.2 на Rails 4.
Вот этот камень pluck_all, чтобы решить эту проблему, сделав поддержку метода pluck_all
не только в Rails 3, но и в Rails 4 и Rails 5. Надеюсь, это поможет тем, кто собирается обновить версию rails.
Ответ 7
Другой вариант:
@user.employees.select(:id, :name).as_json
#=> [{"id" => 1, "name" => "Pete"}, {"id" => 2, "name" => "Fred"}]
Я могу представить, что вы предпочитаете иметь символизированные ключи.
Если этот случай использует метод #symbolize_keys
.
@user.employees.select(:id, :name).as_json.map(&:symbolize_keys)
#=> [{id: 1, name: "Pete"}, {id: 2, name: "Fred"}]
Смотрите: http://api.rubyonrails.org/classes/ActiveModel/Serializers/JSON.html#method-i-as_json