Cómo seleccionar dónde ID en Array Rails ActiveRecord sin excepción

135

Cuando tengo una variedad de identificadores, como

ids = [2,3,5]

y realizo

Comment.find(ids)

Todo funciona bien. Pero cuando hay una identificación que no existe, obtengo una excepción. Esto ocurre generalmente cuando obtengo una lista de ID que coinciden con algún filtro y luego hago algo como

current_user.comments.find(ids)

Esta vez puedo tener una ID de comentario válida, que sin embargo no pertenece a un Usuario dado, por lo que no se encuentra y obtengo una excepción.

Lo he intentado find(:all, ids), pero devuelve todos los registros.

La única forma en que puedo hacerlo ahora es

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

Pero eso me parece una solución súper ineficiente.

¿Hay una mejor manera de seleccionar ID en la matriz sin obtener una excepción en el registro no existente?

Jakub Arnold
fuente

Respuestas:

216

Si solo está evitando la excepción que le preocupa, la familia de funciones "find_all_by .." funciona sin generar excepciones.

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

funcionará incluso si algunos de los identificadores no existen. Esto funciona en el

user.comments.find_all_by_id(potentially_nonexistent_ids)

caso también.

Actualización: Rails 4

Comment.where(id: [2, 3, 5])
prismatodo
fuente
esta es mi solución preferida, parece más limpio que la ruta de manejo de excepciones
Sam azafrán
55
Como otra extensión de esto, en caso de que necesite encadenar condiciones complejas, incluso podría hacer Comment.all (: condiciones => ["aprobado e id en (?)", [1,2,3]])
Omar Qureshi
14
esto quedará en desuso en Rails 4: edgeguides.rubyonrails.org/…
Jonathan Lin
3
@JonathanLin es correcto, se debe preferir la respuesta de mjnissim: stackoverflow.com/a/11457025/33226
Gavin Miller el
66
Esto devuelve un en Arraylugar de un ActiveRecord::Relation, lo que limita lo que puede hacer con él después. Comment.where(id: [2, 3, 5])devuelve un ActiveRecord::Relation.
Joshua Pinter
148

Actualización: esta respuesta es más relevante para Rails 4.x

Hacer esto:

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

El lado más fuerte de este enfoque es que devuelve un Relationobjeto, al que puede unir más .wherecláusulas, .limitcláusulas, etc., lo cual es muy útil. También permite identificaciones inexistentes sin lanzar excepciones.

La sintaxis más nueva de Ruby sería:

current_user.comments.where(id: [123, "456", "Michael Jackson"])
mjnissim
fuente
Gracias por confirmar la wheresintaxis cuando se compara con una matriz. Pensé que podría tener que codificar el SQL con una INdeclaración, pero esto parece más limpio y es un reemplazo fácil para el obsoleto scoped_by_id.
Mark Berry
1
¿Cómo se llama esto y cómo funciona? ¿Es Rails mágico? Como comentó un colega, es como "comparar un número entero con una lista de objetos".
atw
23

Si necesita más control (quizás necesite indicar el nombre de la tabla), también puede hacer lo siguiente:

Model.joins(:another_model_table_name)
  .where('another_model_table_name.id IN (?)', your_id_array)
Jonathan Lin
fuente
Exactamente lo que estaba buscando. ¡Gracias!
Myxtic
¿Hay alguna manera de mantener el orden de your_id_arraycuando recuperas los objetos?
Joshua Pinter
@JoshPinter No creo que esta sea una forma confiable de esperar que la base de datos devuelva cosas en el mismo orden. Quizás agregue una consulta ORDER BY al final para garantizar el orden correcto de las cosas.
Jonathan Lin el
@ JonathanLin Gracias por la respuesta Jonathan. Ciertamente tienes razón. Usar un ORDER BYno funcionará en mi situación porque el orden no se basa en un atributo. Sin embargo, hay una manera de hacerlo a través de SQL (por lo que es rápido) y alguien incluso ha creado una joya para ello. Echa un vistazo a estas preguntas y respuestas: stackoverflow.com/questions/801824/…
Joshua Pinter el
10

Ahora, los métodos .find y .find_by_id están en desuso en los carriles 4. Por lo tanto, podemos usar a continuación:

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

Funcionará incluso si algunos de los identificadores no existen. Esto funciona en el

user.comments.where(id: avoided_ids_array)

También para excluir identificaciones

Comment.where.not(id: [2, 3, 5])
Sumit Munot
fuente
3
Los métodos github.com/rails/activerecord-deprecated_finders .find y .find_by_id NO están en desuso en los rieles 4.
Canna
0

Para evitar excepciones que maten su aplicación, debe detectar esas excepciones y tratarlas de la manera que desee, definiendo el comportamiento de su aplicación en aquellas situaciones en las que no se encuentra la identificación.

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

Aquí hay más información sobre excepciones en ruby.

rogeriopvl
fuente
1
Sí esto resuelve el problema, pero en realidad no es una solución limpia
Jakub Arnold
3
Si va a atrapar una excepción, debe declarar la excepción que espera atrapar, de lo contrario corre el riesgo de que atrape algo que no esperaba y oculte un problema real.
Haegin
0

También puede usarlo en named_scope si desea poner otras condiciones

por ejemplo, incluya algún otro modelo:

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

mtfk
fuente