Jak wybrać, gdzie ID w Array Rails ActiveRecord bez wyjątku

134

Kiedy mam tablicę identyfikatorów, na przykład

ids = [2,3,5]

i występuję

Comment.find(ids)

wszystko dziala. Ale jeśli istnieje identyfikator, który nie istnieje, pojawia się wyjątek. Dzieje się tak zwykle, gdy otrzymuję listę identyfikatorów pasujących do jakiegoś filtra, a potem robię coś podobnego

current_user.comments.find(ids)

Tym razem mogę mieć ważny identyfikator komentarza, który jednak nie należy do danego użytkownika, więc nie został znaleziony i pojawia się wyjątek.

Próbowałem find(:all, ids), ale zwraca wszystkie rekordy.

Jedyny sposób, w jaki mogę to teraz zrobić, to

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

Ale wydaje mi się, że to super nieefektywne rozwiązanie.

Czy istnieje lepszy sposób na wybranie identyfikatora w tablicy bez uzyskiwania wyjątku dla nieistniejącego rekordu?

Jakub Arnold
źródło

Odpowiedzi:

215

Jeśli chodzi tylko o unikanie wyjątku, o który się martwisz, rodzina funkcji „find_all_by ..” działa bez zgłaszania wyjątków.

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

będzie działać, nawet jeśli niektóre identyfikatory nie istnieją. Działa to w

user.comments.find_all_by_id(potentially_nonexistent_ids)

przypadku.

Aktualizacja: Rails 4

Comment.where(id: [2, 3, 5])
pryzmat wszystkiego
źródło
to jest moje preferowane rozwiązanie, wydaje się czystsze niż trasa obsługi wyjątków
Sam Saffron
5
Jako kolejne rozszerzenie tego, jeśli potrzebujesz łańcucha złożonych warunków, możesz nawet zrobić Comment.all (: conditions => ["allowed and id in (?)", [1,2,3]])
Omar Qureshi
14
to zostanie wycofane w Rails 4: edgeguides.rubyonrails.org/…
Jonathan Lin
3
@JonathanLin jest poprawny, preferowana powinna być odpowiedź mjnissima: stackoverflow.com/a/11457025/33226
Gavin Miller
6
Zwraca to Arrayzamiast an ActiveRecord::Relation, co ogranicza to, co możesz później z nim zrobić. Comment.where(id: [2, 3, 5])zwraca plik ActiveRecord::Relation.
Joshua Pinter
147

Aktualizacja: Ta odpowiedź jest bardziej odpowiednia dla Rails 4.x

Zrób to:

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

Silniejszą stroną tego podejścia jest to, że zwraca Relationobiekt, do którego można dołączyć więcej .whereklauzul, .limitklauzul itp., Co jest bardzo pomocne. Pozwala również na nieistniejące identyfikatory bez rzucania wyjątków.

Nowsza składnia Rubiego wyglądałaby tak:

current_user.comments.where(id: [123, "456", "Michael Jackson"])
mjnissim
źródło
Dziękujemy za potwierdzenie whereskładni podczas porównywania z tablicą. Pomyślałem, że być może będę musiał zakodować SQL za pomocą INinstrukcji, ale wygląda to bardziej przejrzysto i jest łatwym zamiennikiem dla przestarzałego scoped_by_id.
Mark Berry
1
Jak to się nazywa i jak działa? Czy to magia Railsów ?! Jak skomentował kolega, przypomina to „porównywanie liczby całkowitej z listą obiektów”.
atw
22

Jeśli potrzebujesz większej kontroli (być może musisz podać nazwę tabeli), możesz również wykonać następujące czynności:

Model.joins(:another_model_table_name)
  .where('another_model_table_name.id IN (?)', your_id_array)
Jonathan Lin
źródło
Dokładnie to, czego szukałem. Dzięki!
Myxtic
Czy jest jakiś sposób, aby zachować kolejność, w your_id_arrayktórej odzyskasz przedmioty?
Joshua Pinter,
@JoshPinter Nie sądzę, aby był to niezawodny sposób, aby oczekiwać, że baza danych będzie zwracać rzeczy w tej samej kolejności. Być może na końcu dodaj zapytanie ORDER BY, aby zapewnić właściwą kolejność rzeczy.
Jonathan Lin
@JonathanLin Dzięki za odpowiedź Jonathan. Masz rację. Użycie ORDER BYnie zadziała w mojej sytuacji, ponieważ kolejność nie jest oparta na atrybucie. Jest jednak sposób na zrobienie tego przez SQL (więc jest szybki) i ktoś nawet stworzył dla niego klejnot. Sprawdź te pytania i odpowiedzi: stackoverflow.com/questions/801824/…
Joshua Pinter
10

Teraz metody .find i .find_by_id są przestarzałe w railsach 4. Zamiast tego możemy użyć poniżej:

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

Będzie działać, nawet jeśli niektóre identyfikatory nie istnieją. Działa to w

user.comments.where(id: avoided_ids_array)

Również do wykluczania identyfikatorów

Comment.where.not(id: [2, 3, 5])
Sumit Munot
źródło
3
Metody github.com/rails/activerecord-deprecated_finders .find i .find_by_id NIE są przestarzałe w rails 4.
Canna
0

Aby uniknąć wyjątków zabijających Twoją aplikację, powinieneś je złapać i traktować tak, jak chcesz, definiując zachowanie aplikacji w sytuacjach, w których nie znaleziono identyfikatora.

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

Oto więcej informacji na temat wyjątków w języku Ruby.

rogeriopvl
źródło
1
tak, to rozwiązuje problem, ale nie jest to naprawdę czyste rozwiązanie
Jakub Arnold,
3
Jeśli zamierzasz złapać wyjątek, powinieneś zadeklarować wyjątek, którego spodziewasz się złapać, w przeciwnym razie ryzykujesz złapanie czegoś, czego się nie spodziewałeś i ukrywanie rzeczywistego problemu.
Haegin
0

Możesz go również użyć w named_scope, jeśli chcesz umieścić tam inne warunki

na przykład dołącz inny model:

named_scope 'get_by_ids', lambda {| ids | {: include => [: comments],: conditions => ["comments.id IN (?)", ids]}}

mtfk
źródło