Как выбрать где 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])

это мое предпочтительное решение, оно кажется чище, чем маршрут обработки исключений
— Сэм Саффрон

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"])

Спасибо за подтверждение 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.


1
да, это решает проблему, но это не совсем чистое решение
— Якуб Арнольд

3
Если вы собираетесь поймать исключение, вам следует объявить исключение, которое вы ожидаете поймать, в противном случае вы рискуете поймать то, чего не ожидали, и скрыть реальную проблему.
— Хаегин

0

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

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

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.