find () avec nil quand il n'y a pas d'enregistrements


95

Dans mon programme de rails actuel, lorsque j'utilise quelque chose comme

 user = User.find(10)

Quand il n'y a pas d'utilisateur avec ID = 10, j'aurai une exception comme:

ActiveRecord::RecordNotFound: Couldn't find User with ID=10

Puis-je obtenir nil au lieu de lever une exception alors quand je fais quelque chose comme:

unless user = Challenge.find(10)
  puts "some error msg"         
end

Je veux juste être nul quand il n'y a pas d'enregistrements et que je ne veux pas utiliser begin / rescue

Merci


Réponses:


170

Oui, faites simplement:

Challenge.find_by_id(10)

Pour les rails 4 et 5:

Challenge.find_by(id: 10)

11
impair! Je n'aurais jamais deviné que le .find_by_*retournerait nul et le .findnon.
ddavison

Cela a changé dans les rails 4, voir cette réponse stackoverflow.com/a/26885027/1438478 pour la nouvelle façon de trouver un élément par un attribut spécifique.
Fralcon

J'ai trouvé un problème étrange avec Rails 4.2 où lorsque vous passez un hachage en tant que «x», Something.find_by(id: x)il créait une instruction SQL avec toutes les paires attribut / valeur du hachage dans le cadre de la clause WHERE. Cela ressemble à un bug de Rails pour moi.
Tilo

Rails (3, 4 ou 5) génère des find_by_...chercheurs dynamiques pour chaque attribut d'un modèle, y compris :id. Donc, Challenge.find_by_id(10)devrait fonctionner quelle que soit la version de Rails.
Arta

Comme spécifié par @MohamedIbrahim ci-dessous, vous pouvez également faire:Challenge.find(10) rescue nil
Hallgeir Wilhelmsen

31

Dans Rails 4, les localisateurs dynamiques - tels que ceux find_by_idutilisés dans la réponse acceptée - étaient obsolètes.

À l'avenir, vous devriez utiliser la nouvelle syntaxe:

Challenge.find_by id: 10

4
Au cas où quelqu'un d'autre serait confus par ceci comme moi: Challenge.find_by(id: 10)est l'autre façon d'écrire ceci
Devin Howard

14

vous pouvez le faire un peu hackish, utilisez simplement l'interface de requête ActiveRecord.

cela retournera nul, au lieu de lever une exception

  User.where(:id => 10).first

Une raison d'utiliser ceci plutôt que find_by_idc'est qu'il est portable des rails 3 à 4. Dans les rails 4, c'est find_by(:id => 10).
Gene

5

Pourquoi n'attrapez-vous pas simplement l'exception? Votre cas ressemble exactement à quelles exceptions ont été faites:

begin
  user = User.find(10)
rescue ActiveRecord::RecordNotFound
  puts "some error msg"
end

Si vous souhaitez récupérer de l'erreur dans le bloc de secours (par exemple en définissant un utilisateur d'espace réservé (modèle nul)), vous pouvez continuer avec votre code sous ce bloc. Sinon, vous pourriez simplement mettre tout votre code pour le "happy case" dans le bloc entre "begin" et "rescue".


Btw: vous n'avez même pas besoin du begin…endbloc, si vous avez déjà un bloc comme une méthode de contrôleur. Dans ce cas, la seule ligne supplémentaire dont vous avez besoin est la rescueligne. Beaucoup plus élégant et plus facile à manipuler que la vérification nilavec une ifdéclaration.
morgler

4

Vous pouvez essayer ceci Challenge.exists?(10)


7
ce sera une requête SQL supplémentaire
fl00r

Même si je pense que c'est mieux chercher pour les tests
SomeSchmo

utilisez-le si vous ne vous souciez pas de la valeur renvoyée mais uniquement de la présence d'un enregistrement dans DB
Filip Bartuzi

4

Pour ceux qui luttent avec MongoId , il se trouve que les deux findet les find_byméthodes soulèvera exception - peu importe votre version de rails!

Il existe une option (à savoir, rise_not_found_error ) qui peut être définie sur false, mais lorsque falsey fait que la findméthode ne déclenche pas d'exception également.

Ainsi, la solution pour les utilisateurs mongoïdes est le code dégoûtant:

User.where(id: 'your_id').first # argghhh

Que pensez-vous de la solution de sauvetage nul de mohamed-ibrahim? Cela .where(email: params[:email]).firstme semble plus élégant que moi.
Wylliam Judd

1
Je préfère ne pas utiliser cette rescuesyntaxe car elle peut masquer des problèmes tels que les fautes de frappe
Cristiano Mendonça

J'ai fini par utiliser la solution de @ morgler.
Wylliam Judd

2

aussi simple que:

user = User.find(10) rescue nil

1
Je préférerais être plus précis sur quelle erreur devrait être sauvée, ActiveRecord :: RecordNotFound dans ce cas. Comme souligné sur cette réponse par @morgler
luizrogeriocn

2
Personnellement, je trouve que c'est la meilleure réponse. Je dis déjà à mon code ce qu'il faut faire si l'utilisateur n'est pas trouvé dansif user...else
Wylliam Judd

0

Vous pouvez utiliser find_by avec l'attribut requis (dans votre cas, l'id), cela retournera nil au lieu de donner une erreur si l'id donné n'est pas trouvé.

user = Challenge.find_by_id(id_value)

ou vous pouvez utiliser le nouveau format:

user = Challenge.find_by id: id_value

Vous pouvez également utiliser where mais vous devez savoir que where renvoie une relation d'enregistrement active avec zéro ou plusieurs enregistrements que vous devez d'abord utiliser pour ne renvoyer qu'un seul enregistrement ou nul si aucun enregistrement ne retourne.

user = Challenge.where(id: id_value).first
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.