Преобразование массива объектов в 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. Вы можете проверить это в консоли рельсов. Это сработало для меня. Я знаю, что это взломать, но в настоящее время я не знаю о рубине, чтобы назначить пустое отношение активной записи к переменной.