Преобразование массива объектов в ActiveRecord:: Relation
У меня есть массив объектов, назовем его Indicator
. Я хочу запустить методы класса Indicator (те из def self.subjects
сорт, области и т.д.) В этом массиве. Единственный способ, которым я знаю, запускать методы класса в группе объектов, - это быть ActiveRecord:: Relation. Поэтому я в конечном итоге прибегает к добавлению метода to_indicators
к Array
.
def to_indicators
# TODO: Make this less terrible.
Indicator.where id: self.pluck(:id)
end
Иногда я цепляю немало таких областей, чтобы отфильтровывать результаты в методах класса. Поэтому, хотя я вызываю метод в ActiveRecord:: Relation, я не знаю, как получить доступ к этому объекту. Я могу добраться до содержимого через all
. Но all
- это массив. Поэтому мне нужно преобразовать этот массив в ActiveRecord:: Relation. Например, это часть одного из методов:
all.to_indicators.applicable_for_bank(id).each do |indicator|
total += indicator.residual_risk_for(id)
indicator_count += 1 if indicator.completed_by?(id)
end
Я думаю, это конденсируется до двух вопросов.
- Как преобразовать массив объектов в ActiveRecord:: Relation? Предпочтительно, не делая
where
каждый раз.
- При запуске метода типа
def self.subjects
в ActiveRecord:: Relation, как мне получить доступ к самому объекту ActiveRecord:: Relation?
Спасибо. Если мне нужно что-то разъяснить, дайте мне знать.
Ответы
Ответ 1
Как преобразовать массив объектов в ActiveRecord:: Relation? Предпочтительно, не делая каждый раз каждый раз.
Вы не можете преобразовать Array в ActiveRecord:: Relation, так как Relation является всего лишь строителем SQL-запроса, и его методы не работают с фактическими данными.
Однако, если вы хотите, это отношение:
-
для ActiveRecord 3.x, не звоните all
и вместо этого вызывайте scoped
, который вернет отношение, которое представляет те же записи, что all
предоставит вам массив.
-
для ActiveRecord 4.x просто вызовите all
, который возвращает Relation.
При запуске метода типа def self.subjects
в ActiveRecord:: Relation, как мне получить доступ к самому объекту ActiveRecord:: Relation?
Когда метод вызывается в объекте Relation, self
- это отношение (в отличие от определенного в нем класса модели).
Ответ 2
Вы можете преобразовать массив объектов arr
в ActiveRecord:: Relation, подобный этому (при условии, что вы знаете, какой класс является объектом, который вы, вероятно, делаете)
MyModel.where(id: arr.map(&:id))
Вы должны использовать where
, хотя это полезный инструмент, который вы не должны неохотно использовать. И теперь у вас есть однострочное преобразование массива в отношение.
map(&:id)
превратит ваш массив объектов в массив, содержащий только их идентификаторы. И передача массива в предложение where будет генерировать оператор SQL с помощью IN
, который выглядит примерно так:
SELECT .... WHERE `my_models`.id IN (2, 3, 4, 6, ....
Имейте в виду, что упорядочение массива будет потеряно. Но поскольку ваша цель состоит только в том, чтобы запустить метод класса в коллекции этих объектов, я предполагаю, что это не будет проблемой.
Ответ 3
Ну, в моем случае мне нужно преобразовать массив объектов в ActiveRecord:: Relation, а также сортировать их с определенным столбцом (например, id), Поскольку я использую MySQL, полевая функция может быть полезна.
MyModel.where('id in (?)',ids).order("field(id,#{ids.join(",")})")
SQL выглядит так:
SELECT ... FROM ... WHERE (id in (11,5,6,7,8,9,10))
ORDER BY field(id,11,5,6,7,8,9,10)
Функциональность поля MySQL
Ответ 4
ActiveRecord::Relation
связывает запрос к базе данных, который извлекает данные из базы данных.
Предположим, что в этом есть смысл. У нас есть массив с объектами одного класса, а затем с каким запросом мы должны связать их?
Когда я бегу,
users = User.where(id: [1,3,4,5])
User Load (0.6ms) SELECT 'users'.* FROM 'users' WHERE 'users'.'id' IN (1, 3, 4, 5) ORDER BY created_at desc
Здесь выше, users
возвращают объект Relation
но связывают запрос базы данных за ним, и вы можете просмотреть его,
users.to_sql
=> "SELECT 'users'.* FROM 'users' WHERE 'users'.'id' IN (1, 3, 4, 5) ORDER BY created_at desc"
Поэтому невозможно вернуть ActiveRecord::Relation
из массива объектов, который не зависит от SQL-запроса.
Ответ 5
Прежде всего, это НЕ серебряная пуля. Исходя из своего опыта, я обнаружил, что преобразование в отношения иногда проще, чем альтернативы. Я стараюсь использовать этот подход очень экономно и только в тех случаях, когда альтернатива будет более сложной.
Это, как говорится, мое решение, я расширил класс Array
# lib/core_ext/array.rb
class Array
def to_activerecord_relation
return ApplicationRecord.none if self.empty?
clazzes = self.collect(&:class).uniq
raise 'Array cannot be converted to ActiveRecord::Relation since it does not have same elements' if clazzes.size > 1
clazz = clazzes.first
raise 'Element class is not ApplicationRecord and as such cannot be converted' unless clazz.ancestors.include? ApplicationRecord
clazz.where(id: self.collect(&:id))
end
end
Ответ 6
Почему бы вам не сохранить объекты в пустой ActiveRecord:: Relation, чтобы вы могли получить к ним доступ. Таким образом
collection = MyModel.where(attribute: nil)
#=> #<ActiveRecord::Relation []>
Примечание. Просто убедитесь, что вы не возвращаете ничего, кроме пустого отношения активной записи.
вы можете затем сохранить свои объекты в коллекции и рассматривать его как обычную коллекцию активных объектов записи вместо того, чтобы выполнять акробатику, участвующую в преобразовании массива объектов в отношение activerecord. Вы можете проверить это в консоли рельсов. Это сработало для меня.
Я знаю, что это взломать, но в настоящее время я не знаю о рубине, чтобы назначить пустое отношение активной записи к переменной.