Як вибрати, де ідентифікатор у 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.