Como selecionar onde o ID no Array Rails ActiveRecord sem exceção

135

Quando tenho uma variedade de IDs, como

ids = [2,3,5]

e eu executo

Comment.find(ids)

tudo funciona bem. Mas quando existe um ID que não existe, recebo uma exceção. Isso ocorre geralmente quando recebo uma lista de IDs que correspondem a algum filtro e, em seguida, faço algo como

current_user.comments.find(ids)

Desta vez, posso ter um ID de comentário válido, que, no entanto, não pertence ao Usuário especificado, portanto não foi encontrado e recebo uma exceção.

Eu tentei find(:all, ids), mas ele retorna todos os registros.

A única maneira de fazer isso agora é

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

Mas isso me parece uma solução super ineficiente.

Existe uma maneira melhor de selecionar o ID na matriz sem obter exceção no registro inexistente?

Jakub Arnold
fonte

Respostas:

216

Se você está apenas evitando a exceção com a qual você está preocupado, a família de funções "find_all_by .." funciona sem gerar exceções.

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

funcionará mesmo se alguns dos IDs não existirem. Isso funciona no

user.comments.find_all_by_id(potentially_nonexistent_ids)

caso também.

Atualização: Rails 4

Comment.where(id: [2, 3, 5])
prisma de tudo
fonte
esta é a minha solução preferida, parece mais limpo do que a rota de tratamento de exceções
Sam Saffron
5
Como outra extensão para isso, se você precisar encadear condições complexas, poderá fazer Comment.all (: conditions => ["aprovado e id em (?)", [1,2,3]])
Omar Qureshi
14
isso será descontinuado no Rails 4: edgeguides.rubyonrails.org/…
Jonathan Lin
3
@JonathanLin está correto, a resposta de mjnissim deve ser a preferida: stackoverflow.com/a/11457025/33226
Gavin Miller
6
Isso retorna um em Arrayvez de um ActiveRecord::Relation, o que limita o que você pode fazer com isso posteriormente. Comment.where(id: [2, 3, 5])retorna um ActiveRecord::Relation.
Joshua Pinter
148

Atualização: Esta resposta é mais relevante para o Rails 4.x

Faça isso:

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

O lado mais forte dessa abordagem é que ela retorna um Relationobjeto ao qual você pode juntar mais .wherecláusulas, .limitcláusulas etc., o que é muito útil. Ele também permite IDs inexistentes sem gerar exceções.

A sintaxe mais recente do Ruby seria:

current_user.comments.where(id: [123, "456", "Michael Jackson"])
mjnissim
fonte
Obrigado por confirmar a wheresintaxe ao comparar com uma matriz. Eu pensei que talvez INfosse necessário codificar o SQL com uma instrução, mas isso parece mais limpo e é um substituto fácil para os obsoletos scoped_by_id.
Mark Berry
1
Como isso é chamado e como funciona? É mágica do Rails ?! Como um colega comentou, é como 'comparar um número inteiro com uma lista de objetos ".
atw
23

Se você precisar de mais controle (talvez precise indicar o nome da tabela), também poderá fazer o seguinte:

Model.joins(:another_model_table_name)
  .where('another_model_table_name.id IN (?)', your_id_array)
Jonathan Lin
fonte
Exatamente o que eu estava procurando. Obrigado!
Myxtic
Existe alguma maneira de manter a ordem de your_id_arrayquando você recuperar os objetos?
Joshua Pinter
@ JoshPinter Não acho que seja uma maneira confiável de esperar que o banco de dados retorne as coisas na mesma ordem. Talvez adicione uma consulta ORDER BY no final para garantir a ordem correta das coisas.
Jonathan Lin
@JonathanLin Obrigado pela resposta Jonathan. Você certamente está certo. Usar um ORDER BYnão funcionará na minha situação porque o pedido não é baseado em um atributo. No entanto, existe uma maneira de fazer isso via SQL (por isso é rápido) e alguém até criou uma jóia para isso. Confira estas perguntas e respostas: stackoverflow.com/questions/801824/…
Joshua Pinter
10

Agora os métodos .find e .find_by_id estão obsoletos no rails 4. Portanto, podemos usar abaixo:

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

Funcionará mesmo se alguns dos IDs não existirem. Isso funciona no

user.comments.where(id: avoided_ids_array)

Também para excluir IDs

Comment.where.not(id: [2, 3, 5])
Sumit Munot
fonte
3
Os métodos github.com/rails/activerecord-deprecated_finders .find e .find_by_id NÃO são preteridos nos trilhos 4.
Canna
0

Para evitar exceções que matam seu aplicativo, você deve capturá-las e tratá-las da maneira que desejar, definindo o comportamento do aplicativo nas situações em que o ID não foi encontrado.

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

Aqui estão mais informações sobre exceções em ruby.

rogeriopvl
fonte
1
sim isso resolve o problema, mas não é realmente uma solução limpa
Jakub Arnold
3
Se você for capturar uma exceção, declare a exceção que espera capturar; caso contrário, corre o risco de pegar algo que não esperava e ocultar um problema real.
Haegin
0

Você também pode usá-lo no named_scope se quiser colocar outras condições

por exemplo, inclua outro modelo:

named_scope 'get_by_ids', lambda {| ids | {: include => [: comentários],: condições => ["comments.id IN (?)", ids]}}

mtfk
fonte