Как выбрать где ID в массиве рельсов ActiveRecord без исключения

135

Когда у меня есть массив идентификаторов, как

ids = [2,3,5]

и я выполняю

Comment.find(ids)

все отлично работает Но когда есть идентификатор, который не существует, я получаю исключение. Обычно это происходит, когда я получаю список идентификаторов, которые соответствуют какому-либо фильтру, а затем я делаю что-то вроде

current_user.comments.find(ids)

На этот раз у меня может быть действительный идентификатор комментария, который, однако, не принадлежит данному пользователю, поэтому он не найден, и я получаю исключение.

Я пытался find(:all, ids), но он возвращает все записи.

Единственный способ сделать это сейчас

current_user.comments.select { |c| ids.include?(c.id) }

Но это мне кажется супер неэффективным решением.

Есть ли лучший способ выбрать ID в массиве без исключения для несуществующей записи?

Якуб Арнольд
источник

Ответы:

216

Если вы просто избегаете исключений, о которых вы беспокоитесь, семейство функций find_all_by .. работает без исключений.

Comment.find_all_by_id([2, 3, 5])

будет работать, даже если некоторые идентификаторы не существуют. Это работает в

user.comments.find_all_by_id(potentially_nonexistent_ids)

дело также.

Обновление: Rails 4

Comment.where(id: [2, 3, 5])
prismofeverything
источник
это мое предпочтительное решение, оно кажется чище, чем маршрут обработки исключений
Сэм Саффрон
5
В качестве еще одного дополнения к этому, если вам нужно связать сложные условия, вы можете даже сделать Comment.all (: условие => ["утверждено и id в (?)", [1,2,3]])
Омар Куреши
14
это будет устаревшим в Rails 4: edgeguides.rubyonrails.org/…
Джонатан Лин
3
@JonathanLin правильный, ответ mjnissim должен быть предпочтительным: stackoverflow.com/a/11457025/33226
Гэвин Миллер
6
Это возвращает Arrayвместо ActiveRecord::Relation, что ограничивает то, что вы можете сделать с ним впоследствии. Comment.where(id: [2, 3, 5])действительно возвращает ActiveRecord::Relation.
Джошуа Пинтер
148

Обновление: этот ответ более актуален для Rails 4.x

Сделай это:

current_user.comments.where(:id=>[123,"456","Michael Jackson"])

Сильной стороной этого подхода является то, что он возвращает Relationобъект, к которому вы можете присоединить больше .whereпредложений, .limitпредложений и т. Д., Что очень полезно. Это также позволяет использовать несуществующие идентификаторы без исключений.

Более новый синтаксис Ruby будет выглядеть так:

current_user.comments.where(id: [123, "456", "Michael Jackson"])
mjnissim
источник
Спасибо за подтверждение whereсинтаксиса при сравнении с массивом. Я подумал, что, возможно, придется кодировать SQL с помощью INоператора, но это выглядит чище и является легкой заменой устаревшего scoped_by_id.
Марк Берри
1
Как это называется и как это работает? Это рельсовая магия ?! Как отметил коллега, его как «сравнивая целое со списком объектов».
ATW
23

Если вам нужно больше контроля (возможно, вам нужно указать имя таблицы), вы также можете сделать следующее:

Model.joins(:another_model_table_name)
  .where('another_model_table_name.id IN (?)', your_id_array)
Джонатан Лин
источник
Именно то, что я искал. Спасибо!
Myxtic
Есть ли способ сохранить порядок your_id_arrayвозврата объектов?
Джошуа Пинтер
@JoshPinter Я не думаю, что это надежный способ ожидать, что база данных будет возвращать вещи в том же порядке. Возможно, добавьте запрос ORDER BY в конце, чтобы обеспечить правильный порядок вещей.
Джонатан Лин,
@JonathanLin Спасибо за ответ Джонатан. Ты конечно прав. Использование не ORDER BYбудет работать в моей ситуации, потому что порядок не основан на атрибуте. Тем не менее, есть способ сделать это через SQL (так что это быстро), и кто-то даже создал драгоценный камень для него. Проверьте это Q & A: stackoverflow.com/questions/801824/…
Джошуа Пинтер
10

Теперь методы .find и .find_by_id устарели в rails 4. Поэтому вместо этого мы можем использовать ниже:

Comment.where(id: [2, 3, 5])

Это будет работать, даже если некоторые идентификаторы не существуют. Это работает в

user.comments.where(id: avoided_ids_array)

Также для исключения идентификаторов

Comment.where.not(id: [2, 3, 5])
Сумит Мунот
источник
3
Методы github.com/rails/activerecord-deprecated_finders .find и .find_by_id НЕ рекомендуются к использованию в рельсах 4.
Canna
0

Чтобы избежать исключений, убивающих ваше приложение, вы должны ловить эти исключения и обрабатывать их так, как вы хотите, определяя поведение вашего приложения в тех ситуациях, когда идентификатор не найден.

begin
  current_user.comments.find(ids)
rescue
  #do something in case of exception found
end

Вот больше информации об исключениях в ruby.

rogeriopvl
источник
1
да, это решает проблему, но это не совсем чистое решение
Якуб Арнольд
3
Если вы собираетесь поймать исключение, вам следует объявить исключение, которое вы ожидаете поймать, в противном случае вы рискуете поймать то, чего не ожидали, и скрыть реальную проблему.
Хаегин
0

Вы также можете использовать его в named_scope, если хотите поставить другие условия

Например, включите какую-нибудь другую модель:

named_scope 'get_by_ids', lambda {| ids | {: include => [: comments],: условие => ["comments.id IN (?)", идентификаторы]}}

mtfk
источник