Come selezionare dove ID in Array Rails ActiveRecord senza eccezioni


135

Quando ho una serie di ID, come

ids = [2,3,5]

e mi esibisco

Comment.find(ids)

tutto funziona bene. Ma quando esiste un ID che non esiste, ottengo un'eccezione. Ciò si verifica generalmente quando ottengo un elenco di ID che corrispondono ad alcuni filtri e che faccio qualcosa del genere

current_user.comments.find(ids)

Questa volta potrei avere un ID commento valido, che tuttavia non appartiene a un determinato Utente, quindi non viene trovato e ottengo un'eccezione.

Ho provato find(:all, ids), ma restituisce tutti i record.

L'unico modo in cui posso farlo ora è

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

Ma questa mi sembra una soluzione super inefficiente.

Esiste un modo migliore per selezionare l' ID nell'array senza ottenere eccezioni su record inesistenti?

Risposte:


216

Se si sta semplicemente evitando l'eccezione di cui si è preoccupati, la famiglia di funzioni "find_all_by .." funziona senza generare eccezioni.

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

funzionerà anche se alcuni degli ID non esistono. Questo funziona nel

user.comments.find_all_by_id(potentially_nonexistent_ids)

anche caso.

Aggiornamento: Rotaie 4

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

questa è la mia soluzione preferita, sembra più pulita della via di gestione delle eccezioni
Sam Saffron,

5
Come ulteriore estensione, se dovessi incatenare condizioni complesse, potresti persino fare Comment.all (: condizioni => ["approvato e id in (?)", [1,2,3]])
Omar Qureshi

14
questo sarà deprecato in Rails 4: edgeguides.rubyonrails.org/…
Jonathan Lin

3
@JonathanLin è corretto, la risposta di mjnissim dovrebbe essere preferita: stackoverflow.com/a/11457025/33226
Gavin Miller

6
Questo restituisce un Arrayinvece di un ActiveRecord::Relation, il che limita ciò che puoi fare in seguito. Comment.where(id: [2, 3, 5])restituisce un ActiveRecord::Relation.
Joshua Pinter,

148

Aggiornamento: questa risposta è più pertinente per Rails 4.x

Fai questo:

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

Il lato più forte di questo approccio è che restituisce un Relationoggetto, al quale è possibile unire più .whereclausole, .limitclausole, ecc., Che è molto utile. Consente inoltre ID inesistenti senza generare eccezioni.

La nuova sintassi di Ruby sarebbe:

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

Grazie per aver confermato la wheresintassi durante il confronto con un array. Ho pensato che avrei dovuto codificare l'SQL con INun'istruzione, ma questo sembra più pulito ed è un facile sostituto per il deprecato scoped_by_id.
Mark Berry,

1
Come si chiama e come funziona? È Rails magico ?! Come ha commentato un collega, è come "confrontare un intero con un elenco di oggetti".
Atw

23

Se hai bisogno di più controllo (forse devi indicare il nome della tabella) puoi anche fare quanto segue:

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

Esattamente quello che stavo cercando. Grazie!
Myxtic,

C'è un modo per mantenere l'ordine del your_id_arrayquando si riprendono gli oggetti?
Joshua Pinter,

@JoshPinter Non penso che questo sia un modo affidabile per aspettarsi che il database restituisca le cose nello stesso ordine. Forse aggiungere una query ORDER BY alla fine per garantire il giusto ordine delle cose.
Jonathan Lin,

@JonathanLin Grazie per la risposta Jonathan. Hai certamente ragione. L'uso di an ORDER BYnon funzionerà nella mia situazione perché l'ordine non si basa su un attributo. Tuttavia, c'è un modo per farlo tramite SQL (quindi è veloce) e qualcuno ha persino creato una gemma per questo. Dai un'occhiata a queste domande e risposte
Joshua Pinter

10

Ora i metodi .find e .find_by_id sono deprecati nelle rotaie 4. Quindi invece possiamo usare di seguito:

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

Funzionerà anche se alcuni degli ID non esistono. Questo funziona nel

user.comments.where(id: avoided_ids_array)

Anche per escludere gli ID

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

3
I metodi github.com/rails/activerecord-deprecated_finders .find e .find_by_id NON sono deprecati nei binari 4.
Canna

0

Per evitare eccezioni che uccidono la tua app, dovresti cogliere tali eccezioni e trattarle come desideri, definendo il comportamento per la tua app in quelle situazioni in cui non viene trovato l'ID.

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

Ecco maggiori informazioni sulle eccezioni in ruby.


1
sì, questo risolve il problema, ma non è proprio una soluzione pulita
Jakub Arnold,

3
Se stai per rilevare un'eccezione, devi dichiarare l'eccezione che prevedi di rilevare, altrimenti rischi di catturare qualcosa che non ti aspettavi e nascondere un problema reale.
Haegin,

0

Puoi anche usarlo in named_scope se vuoi mettere lì altre condizioni

per esempio includere qualche altro modello:

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

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.