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

Eqbal
la source

Réponses:

170

Oui, faites simplement:

Challenge.find_by_id(10)

Pour les rails 4 et 5:

Challenge.find_by(id: 10)
apnée
la source
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
hattila91
la source
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
bonnet
la source
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".

Morgler
la source
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)

tonymarschall
la source
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
Cristiano Mendonça
la source
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
mohamed-ibrahim
la source
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
Alanoud Just
la source