Як вибрати, де ідентифікатор у Array Rails ActiveRecord без винятку


134

Коли у мене є масив ідентифікаторів, як

ids = [2,3,5]

і я виконую

Comment.find(ids)

все працює добре. Але коли є ідентифікатор, якого не існує, я отримую виняток. Це трапляється в основному, коли я отримую список ідентифікаторів, які відповідають якомусь фільтру, і я роблю щось подібне

current_user.comments.find(ids)

Цього разу у мене може бути дійсний ідентифікатор коментаря, який, однак, не належить даному Користувачеві, тому його не знайдено, і я отримаю виняток.

Я спробував find(:all, ids), але він повертає всі записи.

Єдиний спосіб, коли я це можу зробити, це

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

Але це здається мені надзвичайно неефективним рішенням.

Чи є кращий спосіб вибрати ідентифікатор в масиві без отримання винятку з неіснуючої записи?

Відповіді:


215

Якщо ви просто уникаєте виключення, про яке ви переживаєте, сімейство функцій "find_all_by .." працює без викидів винятків.

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

буде працювати, навіть якщо деяких ідентифікаторів не існує. Це працює в

user.comments.find_all_by_id(potentially_nonexistent_ids)

справа також.

Оновлення: рейки 4

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

це моє вподобане рішення, воно здається більш чистим, ніж маршрут оброблення винятків
Сем Сафрон

5
В якості іншого розширення до цього, ви повинні мати потребу в ланцюзі складних умовах, можна навіть зробити Comment.all (: умови => [ «затверджений і ідентифікатор в ()?», [1,2,3]])
Омар Куреши

14
це буде застаріло в Rails 4: edgeguides.rubyonrails.org/…
Джонатан Лін

3
@JonathanLin правильно, відповідь mjnissim повинен бути краще: stackoverflow.com/a/11457025/33226
Gavin Miller

6
Це повертає Arrayзамість того ActiveRecord::Relation, що обмежує те, що ви можете зробити з ним згодом. Comment.where(id: [2, 3, 5])повертає ActiveRecord::Relation.
Джошуа Пінтер

147

Оновлення: ця відповідь більше стосується 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
Як це називається і як це працює? Це Rails магія ?! Як зауважив колега, це схоже на "порівняння цілого числа зі списком об'єктів".
atw

22

Якщо вам потрібен додатковий контроль (можливо, вам потрібно вказати ім’я таблиці), ви також можете зробити наступне:

Model.joins(:another_model_table_name)
  .where('another_model_table_name.id IN (?)', your_id_array)

Саме те, що я шукав. Дякую!
Мікст

Чи є спосіб зберегти порядок, your_id_arrayколи ви отримуєте об'єкти назад?
Джошуа Пінтер

@JoshPinter Я не думаю, що це надійний спосіб очікувати, що база даних поверне речі в тому ж порядку. Можливо, в кінці додайте запит ЗАМОВИТИ, щоб забезпечити правильний порядок речей.
Джонатан Лін

@JonathanLin Дякую за відповідь Джонатан. Ви, звичайно, праві. Використання значка ORDER BYне працює в моїй ситуації, оскільки замовлення не базується на атрибуті. Однак є спосіб зробити це через SQL (так це швидко), і хтось навіть створив дорогоцінний камінь для цього. Ознайомтесь із цим запитанням: stackoverflow.com/questions/801824/…
Джошуа Пінтер

10

Тепер методи .find і .find_by_id застаріли в рейках 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

Ось додаткові відомості про винятки в рубіні.


1
так, це вирішує проблему, але це не дуже чисте рішення
Якуб Арнольд,

3
Якщо ви збираєтеся зловити виняток, вам слід оголосити виняток, який ви очікуєте зловити, інакше ви ризикуєте схопити щось, чого ви не очікували, і приховати справжню проблему.
Гегін

0

Ви також можете використовувати його в name_scope, якщо ви хочете поставити там інші умови

наприклад, включіть якусь іншу модель:

найменування_scope 'get_by_ids', лямбда {| ids | {: include => [: коментарі],: умови => ["comments.id IN (?)", id]}}

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.