Comment sélectionner l'emplacement de l'ID dans Array Rails ActiveRecord sans exception

135

Quand j'ai un tableau d'identifiants, comme

ids = [2,3,5]

et je joue

Comment.find(ids)

tout fonctionne bien. Mais quand il y a un identifiant qui n'existe pas, j'obtiens une exception. Cela se produit généralement lorsque j'obtiens une liste d'identifiants qui correspondent à un filtre et que je fais quelque chose comme

current_user.comments.find(ids)

Cette fois, je peux avoir un identifiant de commentaire valide, qui n'appartient cependant pas à l'utilisateur donné, donc il n'est pas trouvé et j'obtiens une exception.

J'ai essayé find(:all, ids), mais il renvoie tous les enregistrements.

La seule façon dont je peux le faire maintenant est

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

Mais cela me semble être une solution super inefficace.

Existe-t-il un meilleur moyen de sélectionner l' ID dans Array sans obtenir une exception sur un enregistrement non existant?

Jakub Arnold
la source

Réponses:

216

S'il s'agit simplement d'éviter l'exception qui vous inquiète, la famille de fonctions «find_all_by ..» fonctionne sans lever d'exceptions.

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

fonctionnera même si certains des identifiants n'existent pas. Cela fonctionne dans le

user.comments.find_all_by_id(potentially_nonexistent_ids)

cas aussi.

Mise à jour: Rails 4

Comment.where(id: [2, 3, 5])
prisme de tout
la source
c'est ma solution préférée, elle semble plus propre que la route de gestion des exceptions
Sam Saffron
5
Comme autre extension à cela, si vous avez besoin d'enchaîner des conditions complexes, vous pouvez même faire Comment.all (: conditions => ["approuvé et id dans (?)", [1,2,3]])
Omar Qureshi
14
cela sera obsolète dans Rails 4: edgeguides.rubyonrails.org
Jonathan Lin
3
@JonathanLin est correct, la réponse de mjnissim devrait être préférée: stackoverflow.com/a/11457025/33226
Gavin Miller
6
Cela renvoie un Arrayau lieu d'un ActiveRecord::Relation, ce qui limite ce que vous pouvez en faire par la suite. Comment.where(id: [2, 3, 5])renvoie un ActiveRecord::Relation.
Joshua Pinter
148

Mise à jour: cette réponse est plus pertinente pour Rails 4.x

Faites ceci:

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

Le côté fort de cette approche est qu'elle renvoie un Relationobjet, auquel vous pouvez joindre plus de .whereclauses, .limitclauses, etc., ce qui est très utile. Il autorise également les ID inexistants sans lever d'exceptions.

La nouvelle syntaxe Ruby serait:

current_user.comments.where(id: [123, "456", "Michael Jackson"])
mjnissim
la source
Merci d'avoir confirmé la wheresyntaxe lors de la comparaison avec un tableau. Je pensais que je pourrais devoir coder le SQL avec une INinstruction, mais cela semble plus propre et remplace facilement les obsolètes scoped_by_id.
Mark Berry
1
Comment ça s'appelle et comment ça marche? Est-ce la magie des Rails?! Comme l'a commenté un collègue, c'est comme "comparer un entier avec une liste d'objets".
atw
23

Si vous avez besoin de plus de contrôle (peut-être devez-vous indiquer le nom de la table), vous pouvez également effectuer les opérations suivantes:

Model.joins(:another_model_table_name)
  .where('another_model_table_name.id IN (?)', your_id_array)
Jonathan Lin
la source
Exactement ce que je cherchais. Merci!
Myxtic
Existe-t-il un moyen de conserver l'ordre du your_id_arraymoment où vous récupérez les objets?
Joshua Pinter
@JoshPinter Je ne pense pas que ce soit un moyen fiable de s'attendre à ce que la base de données renvoie les choses dans le même ordre. Ajoutez peut-être une requête ORDER BY à la fin pour garantir le bon ordre des choses.
Jonathan Lin
@JonathanLin Merci pour la réponse Jonathan. Vous avez certainement raison. L'utilisation d'un ORDER BYne fonctionnera pas dans ma situation car la commande n'est pas basée sur un attribut. Cependant, il existe un moyen de le faire via SQL (donc c'est rapide) et quelqu'un a même créé un bijou pour cela. Consultez ce Q&A: stackoverflow.com/questions/801824/…
Joshua Pinter
10

Maintenant, les méthodes .find et .find_by_id sont obsolètes dans les rails 4. Nous pouvons donc utiliser ci-dessous:

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

Cela fonctionnera même si certains des identifiants n'existent pas. Cela fonctionne dans le

user.comments.where(id: avoided_ids_array)

Aussi pour exclure les ID

Comment.where.not(id: [2, 3, 5])
Sumit Munot
la source
3
github.com/rails/activerecord-deprecated_finders Les méthodes .find et .find_by_id ne sont PAS obsolètes dans les rails 4.
Canna
0

Pour éviter que les exceptions ne tuent votre application, vous devez attraper ces exceptions et les traiter comme vous le souhaitez, en définissant le comportement de votre application dans les situations où l'identifiant n'est pas trouvé.

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

Voici plus d'informations sur les exceptions en rubis.

rogeriopvl
la source
1
oui, cela résout le problème, mais ce n'est pas vraiment une solution propre
Jakub Arnold
3
Si vous allez attraper une exception, vous devez déclarer l'exception que vous vous attendez à attraper, sinon vous risquez qu'elle attrape quelque chose que vous ne vous attendiez pas et cache un problème réel.
Haegin
0

Vous pouvez également l'utiliser dans named_scope si vous souhaitez y mettre d'autres conditions

par exemple, incluez un autre modèle:

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

mtfk
la source