2013-06-26 4 views
77

У меня есть массив объектов, назовем его Indicator. Я хочу запустить методы класса индикатора (те из класса , области действия и т. Д.) В этом массиве. Единственный способ, которым я знаю, запускать методы класса в группе объектов, - это быть ActiveRecord :: Relation. Поэтому я в конечном итоге прибегает к добавлению метода to_indicators к Array.Преобразование массива объектов в ActiveRecord :: Relation

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?

Спасибо. Если мне нужно что-то разъяснить, дайте мне знать.

+2

Если ваша единственная причина для пытаться преобразовать этот массив обратно в отношения, потому что вы получили его через '.all', просто используйте' .scoped' как ответ Эндрю Маршалл указывает (Хотя в рельсах 4, он будет работать с ' .all'). Если вам нужно превратить массив в отношение, которое вы ошиблись где-то ... – nzifnab

ответ

37

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

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

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

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

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

При выполнении метода def self.subjects типа на в ActiveRecord :: Relation, как я могу получить доступ, что сам ActiveRecord :: Отношение объекта?

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

+1

См. @Marco Prins ниже о решении. – Justin

+0

насчет .С устаревшего метода в рельсах 5 – Jaswinder

+0

@GstjiSaini Я не уверен в точном методе вы имеете в виду, пожалуйста, предоставьте документ или ссылка на источник. Хотя, если это устарело, это не очень жизнеспособное решение, так как оно, скорее всего, скоро исчезнет. –

116

Вы можете преобразовать массив объектов arr к ActiveRecord :: Relation, как это (если вы знаете, какой класс объектов являются, которые вы, вероятно)

MyModel.where(id: arr.map(&:id)) 

Вы должны использовать where, хотя, это полезный инструмент, который вы не должны неохотно использовать. И теперь у вас есть однострочное преобразование массива в отношение.

map(&:id) превратит ваш массив объектов в массив, содержащий только их идентификаторы. И передавая массив, где положение будет генерировать SQL заявление с IN который выглядит примерно так:

SELECT .... WHERE `my_models`.id IN (2, 3, 4, 6, .... 

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

+2

Молодцы, именно то, что мне нужно. Вы можете использовать это для любого атрибута модели. отлично работает для меня. – nfriend21

+7

Зачем строить буквальный SQL самостоятельно, когда вы можете делать 'where (id: arr.map (&: id))'? И, строго говоря, это не преобразует массив в отношение, а вместо этого получает новые экземпляры объектов (после реализации отношения) с теми идентификаторами, которые могут иметь разные значения атрибутов, чем те, которые уже присутствуют в памяти в массиве. –

+6

Это, однако, теряет порядок. –

2

Ну, в моем случае, мне нужно преобразования массив объектов ActiveRecord :: Relation, а также сортировка их с определенным идентификатором столбца (например). Поскольку я использую 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 field function

+0

Для версии этого, которая работает с PostgreSQL, посмотрите не дальше этой темы: https://stackoverflow.com/questions/1309624/simulating-mysqls-order-by-field-in-postgresql – armchairdj

-1

Почему вы не храните вас объекты в пустой ActiveRecord :: Relation, так что вы можете иметь доступ к ним. Вроде так

collection = MyModel.where(attribute: nil) 
#=> #<ActiveRecord::Relation []> 

Примечание: просто убедитесь, что вы не возвращаете ничего, кроме пустого отношения активной записи. вы можете сохранять свои объекты в коллекции и рассматривать его как обычный сбор активных записей объектов вместо того, чтобы выполнять акробатические, участвующие в преобразовании массива объектов в ActiveRecord отношение. Вы можете проверить это в консоли рельсов. Это сработало для меня. Я знаю, что это взломать, но в настоящее время я не знаю о рубиновом способе присвоить пустое отношение активной записи к переменной.