it-roy-ru.com

Преобразование массива объектов в 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

Я предполагаю, что это сводится к двум вопросам.

  1. Как я могу преобразовать массив объектов в ActiveRecord :: Relation? Желательно, не делая where каждый раз.
  2. При запуске метода типа def self.subjects в ActiveRecord :: Relation, как мне получить доступ к самому объекту ActiveRecord :: Relation?

Благодарю. Если мне нужно что-то уточнить, дайте мне знать.

88
Nathan

Как я могу преобразовать массив объектов в ActiveRecord :: Relation? Желательно, не делая, где каждый раз.

Вы не можете преобразовать массив в ActiveRecord :: Relation, поскольку Relation является просто компоновщиком для запроса SQL, а его методы не работают с реальными данными.

Однако, если вы хотите отношения, то:

  • для ActiveRecord 3.x не вызывайте all, а вместо этого вызывайте scoped , который вернет Relation, представляющий те же записи, которые all дал бы вам в массиве.

  • для ActiveRecord 4.x просто вызовите all , который возвращает отношение.

При запуске метода типа def self.subjects в ActiveRecord :: Relation как получить доступ к самому объекту ActiveRecord :: Relation?

Когда метод вызывается для объекта Relation, self - это отношение (в отличие от класса модели, в котором он определен).

42
Andrew Marshall

Вы можете преобразовать массив объектов 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, ....

Имейте в виду, что упорядочение массива будет потеряно - Но так как ваша цель - только запустить метод класса на collection этих объектов, я предполагаю, что это не будет проблемой.

140
Marco Prins

Ну, в моем случае мне нужно преобразовать массив объектов в ActiveRecord :: Relation, а также сортировать их с определенным столбцом (например, id). Поскольку я использую MySQL, функция field может быть полезна.

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
Xingcheng Xia

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

Здесь выше, usersreturn Relation объект, но связывает запрос базы данных за ним, и вы можете просмотреть его,

users.to_sql
 => "SELECT `users`.* FROM `users` WHERE `users`.`id` IN (1, 3, 4, 5)  ORDER BY created_at desc"

Таким образом, невозможно вернуть ActiveRecord::Relation из массива объектов, который не зависит от SQL-запроса.

0
ray

Прежде всего, это НЕ серебряная пуля. Исходя из своего опыта, я обнаружил, что преобразование в отношения иногда проще, чем альтернативы. Я стараюсь использовать этот подход очень экономно и только в тех случаях, когда альтернатива будет более сложной.

Это, как говорится, мое решение, я расширил класс 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

Пример использования будет array.to_activerecord_relation.update_all(status: 'finished'). Теперь, где я могу это использовать?

Иногда вам нужно отфильтровать ActiveRecord::Relation, например, удалить незавершенные элементы. В этих случаях лучше всего использовать область elements.not_finished, и вы все равно сохраните ActiveRecord::Relation.

Но иногда это условие является более сложным. Извлеките все элементы, которые не были закончены, и которые были произведены за последние 4 недели и были проверены. Чтобы избежать создания новых областей, вы можете отфильтровать массив, а затем преобразовать обратно. Имейте в виду, что вы по-прежнему выполняете запрос к БД, так как он выполняет поиск по id, но все же выполняет запрос.

0
Haris Krajina