trouver vs find_by vs où

127

Je suis nouveau sur les rails. Ce que je vois, c'est qu'il existe de nombreuses façons de trouver un disque:

  1. find_by_<columnname>(<columnvalue>)
  2. find(:first, :conditions => { <columnname> => <columnvalue> }
  3. where(<columnname> => <columnvalue>).first

Et il semble que tous finissent par générer exactement le même SQL. De plus, je pense que la même chose est vraie pour trouver plusieurs enregistrements:

  1. find_all_by_<columnname>(<columnvalue>)
  2. find(:all, :conditions => { <columnname> => <columnvalue> }
  3. where(<columnname> => <columnvalue>)

Existe-t-il une règle empirique ou une recommandation sur laquelle utiliser?

Victor Ronin
la source

Réponses:

103

Utilisez celui qui vous semble le mieux adapté à vos besoins.

La findméthode est généralement utilisée pour récupérer une ligne par ID:

Model.find(1)

Il est à noter que findcela lèvera une exception si l'élément n'est pas trouvé par l'attribut que vous fournissez. Utilisez where(comme décrit ci-dessous, qui renverra un tableau vide si l'attribut n'est pas trouvé) pour éviter qu'une exception ne soit levée.

D'autres utilisations de findsont généralement remplacées par des choses comme ceci:

Model.all
Model.first

find_byest utilisé comme une aide lorsque vous recherchez des informations dans une colonne, et il correspond à celles-ci avec des conventions de dénomination. Par exemple, si vous avez une colonne nommée namedans votre base de données, vous utiliserez la syntaxe suivante:

Model.find_by(name: "Bob")

.where est plus un fourre-tout qui vous permet d'utiliser une logique un peu plus complexe lorsque les helpers conventionnels ne le font pas, et il renvoie un tableau d'éléments qui correspondent à vos conditions (ou un tableau vide dans le cas contraire).

John
la source
62
find_byn'est pas obsolète, mais la syntaxe change un peu. Du find_by_name("Bob")au find_by(:name, "Bob").
Brian Morearty
61
@BrianMorearty Je crois que vous vouliez direfind_by(name: "Bob")
MCB
1
@BrianMorearty Je n'ai trouvé aucune preuve que je suis find_by_...obsolète, avez-vous une source? Il semble find_byet find_by_...sont toujours pris en charge dans Rails 4.
Dennis
4
@Dennis, vous avez raison de dire que ce n'est pas obsolète, et c'est ce que j'ai dit. Mais j'aurais pu être plus clair quand j'ai dit «mais la syntaxe change un peu». Ce que je voulais dire, c'est "mais une nouvelle syntaxe est également disponible maintenant". Voir la correction de MCB pour la nouvelle syntaxe.
Brian Morearty
3
C'est ce qui est mentionné dans la version de rails 4.0 "Toutes les méthodes dynamiques à l'exception de find_by _... et find_by _...! Sont obsolètes" plus de détails sur edgeguides.rubyonrails.org
Mukesh Singh Rathaur
131

renvoie ActiveRecord :: Relation

Jetez maintenant un œil à l'implémentation de find_by:

def find_by
  where(*args).take
end

Comme vous pouvez le voir, find_by est identique à where mais il ne renvoie qu'un seul enregistrement. Cette méthode doit être utilisée pour obtenir 1 enregistrement et doit être utilisée pour obtenir tous les enregistrements avec certaines conditions.

Mike Andrianov
la source
1
find_by renvoie un objet, mais où renvoie une collection.
Kick Buttowski
Lorsque la valeur requête hors de portée, find_bysauvera ::RangeErrorde where(*args) et nul retour.
fangxing
34

Model.find

1- Paramètre: ID de l'objet à rechercher.

2- Si trouvé: Il renvoie l'objet (un seul objet).

3- Si non trouvé: déclenche une ActiveRecord::RecordNotFoundexception.

Model.find_by

1- Paramètre: clé / valeur

Exemple:

User.find_by name: 'John', email: '[email protected]'

2- Si trouvé: il renvoie l'objet.

3- Si non trouvé: retourne nil.

Remarque: si vous souhaitez augmenter l'ActiveRecord::RecordNotFoundutilisationfind_by!

Model.where

1- Paramètre: identique à find_by

2- Si trouvé: Il retourne ActiveRecord::Relationcontenant un ou plusieurs enregistrements correspondant aux paramètres.

3- Si non trouvé: il renvoie un Empty ActiveRecord::Relation.

Hossam Khamis
la source
31

Il y a une différence entre findet find_byqui findrenverra une erreur si elle n'est pas trouvée, alors que find_byretournera null.

Parfois, il est plus facile à lire si vous avez une méthode comme find_by email: "haha", par opposition à .where(email: some_params).first.

Kasumi
la source
17

Depuis Rails 4, vous pouvez faire:

User.find_by(name: 'Bob')

qui est l'équivalent find_by_namedans Rails 3.

À utiliser #wherequand #findet #find_byne suffit pas.

Agis
la source
2
Agis, je suis d'accord avec vous, mais j'ai cherché sur Internet pourquoi devrions-nous utiliser find_byet non find_by_<column_name>. J'en ai besoin pour répondre à quelqu'un.
KULKING
Agis, avez-vous des sources pour étayer votre affirmation selon laquelle nous ne devrions plus utiliser find_by_namedans Rails 4? Autant que je sache, ce n'est pas obsolète .
Dennis
Y a-t-il un moyen simple de le faire, mais disons que le nom contient un caractère générique, alors quelque chose commefind_by(name: "Rob*")
Batman
1
@Dennis Il est possible d'utiliser les deux, ils sont valides. Je préfère la nouvelle syntaxe car l'API est plus intuitive à mon humble avis. C'est comme ça que je le concevoir moi-même :)
Agis
8

La réponse acceptée couvre généralement tout, mais j'aimerais ajouter quelque chose, juste au cas où vous prévoyez de travailler avec le modèle d'une manière comme une mise à jour, et que vous récupérez un seul enregistrement (dont idvous ne savez pas), alors find_byest la voie à suivre, car il récupère l'enregistrement et ne le met pas dans un tableau

irb(main):037:0> @kit = Kit.find_by(number: "3456")
  Kit Load (0.9ms)  SELECT "kits".* FROM "kits" WHERE "kits"."number" = 
 '3456' LIMIT 1
=> #<Kit id: 1, number: "3456", created_at: "2015-05-12 06:10:56",   
updated_at: "2015-05-12 06:10:56", job_id: nil>

irb(main):038:0> @kit.update(job_id: 2)
(0.2ms)  BEGIN Kit Exists (0.4ms)  SELECT 1 AS one FROM "kits" WHERE  
("kits"."number" = '3456' AND "kits"."id" != 1) LIMIT 1 SQL (0.5ms)   
UPDATE "kits" SET "job_id" = $1, "updated_at" = $2 WHERE  "kits"."id" = 
1  [["job_id", 2], ["updated_at", Tue, 12 May 2015 07:16:58 UTC +00:00]] 
(0.6ms)  COMMIT => true

mais si vous utilisez, wherevous ne pouvez pas le mettre à jour directement

irb(main):039:0> @kit = Kit.where(number: "3456")
Kit Load (1.2ms)  SELECT "kits".* FROM "kits" WHERE "kits"."number" =  
'3456' => #<ActiveRecord::Relation [#<Kit id: 1, number: "3456", 
created_at: "2015-05-12 06:10:56", updated_at: "2015-05-12 07:16:58", 
job_id: 2>]>

irb(main):040:0> @kit.update(job_id: 3)
ArgumentError: wrong number of arguments (1 for 2)

dans un tel cas, vous devrez le spécifier comme ceci

irb(main):043:0> @kit[0].update(job_id: 3)
(0.2ms)  BEGIN Kit Exists (0.6ms)  SELECT 1 AS one FROM "kits" WHERE 
("kits"."number" = '3456' AND "kits"."id" != 1) LIMIT 1 SQL (0.6ms)   
UPDATE "kits" SET "job_id" = $1, "updated_at" = $2 WHERE "kits"."id" = 1  
[["job_id", 3], ["updated_at", Tue, 12 May 2015 07:28:04 UTC +00:00]]
(0.5ms)  COMMIT => true
Leonard Kakande
la source
exactement ce que je cherchais
Paul Brunache
2
@kit = Kit.where (numéro: "3456"). first - Cela vous permet de le mettre à jour directement et c'est plus sûr car il a survécu à la dépréciation
Paul Brunache
6

En dehors de la réponse acceptée, ce qui suit est également valide

Model.find()peut accepter un tableau d'identifiants et renverra tous les enregistrements qui correspondent. Model.find_by_id(123)accepte également le tableau mais ne traitera que la première valeur d'identifiant présente dans le tableau

Model.find([1,2,3])
Model.find_by_id([1,2,3])
Saumya Mehta
la source
3

Les réponses données jusqu'à présent sont toutes correctes.

Cependant, une différence intéressante est que les Model.findrecherches par identifiant; s'il est trouvé, il renvoie un Modelobjet (juste un seul enregistrement) mais renvoie un ActiveRecord::RecordNotFoundsinon.

Model.find_byest très similaire à Model.findet vous permet de rechercher n'importe quelle colonne ou groupe de colonnes dans votre base de données, mais il renvoie nilsi aucun enregistrement ne correspond à la recherche.

Model.whered'autre part, renvoie un Model::ActiveRecord_Relationobjet qui ressemble à un tableau contenant tous les enregistrements qui correspondent à la recherche . Si aucun enregistrement n'a été trouvé, il renvoie un Model::ActiveRecord_Relationobjet vide .

J'espère que cela vous aidera à décider lequel utiliser à tout moment.

échelon
la source
3

Supposons que j'ai un modèle User

  1. User.find(id)

Renvoie une ligne où clé primaire = id. Le type de retour sera Userobject.

  1. User.find_by(email:"[email protected]")

Renvoie la première ligne avec l'attribut ou l'e-mail correspondant dans ce cas. Le type de retour sera à Usernouveau objet.

Remarque: - User.find_by(email: "[email protected]")est similaire àUser.find_by_email("[email protected]")

  1. User.where(project_id:1)

Renvoie tous les utilisateurs de la table users où l'attribut correspond.

Ici, le type de retour sera ActiveRecord::Relationobject. ActiveRecord::RelationLa classe inclut le Enumerablemodule de Ruby afin que vous puissiez utiliser son objet comme un tableau et parcourir dessus.

Nikhil Mohadikar
la source
0

La meilleure partie de l'utilisation de toute technologie open source est que vous pouvez en inspecter la longueur et l'étendue. Découvrez ce lien

find_by ~> Recherche le premier enregistrement correspondant aux conditions spécifiées. Il n'y a pas d'ordre implicite, donc si l'ordre est important, vous devez le spécifier vous-même. Si aucun enregistrement n'est trouvé, renvoie nil.

find ~> Recherche le premier enregistrement correspondant aux conditions spécifiées, mais si aucun enregistrement n'est trouvé, cela déclenche une exception mais cela est fait délibérément.

Consultez le lien ci-dessus, il contient toutes les explications et les cas d'utilisation des deux fonctions suivantes.

NIshank
la source
-5

Je recommanderai personnellement d'utiliser

where(< columnname> => < columnvalue>)
Prateek Alakh
la source
1
Cela peut répondre à la question. Mais essayez de décrire les avantages et les inconvénients de l'utilisation de votre approche, plutôt que la réponse acceptée.
Vivek Kumar