find vs find_by vs where

127

Soy nuevo en rieles. Lo que veo es que hay muchas maneras de encontrar un registro:

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

Y parece que todos terminan generando exactamente el mismo SQL. Además, creo que lo mismo es cierto para encontrar múltiples registros:

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

¿Hay alguna regla general o recomendación sobre cuál usar?

Victor Ronin
fuente

Respuestas:

103

Use el que sienta que mejor se adapte a sus necesidades.

El findmétodo se usa generalmente para recuperar una fila por ID:

Model.find(1)

Vale la pena señalar que findarrojará una excepción si el atributo que proporciona no encuentra el artículo. Use where(como se describe a continuación, que devolverá una matriz vacía si no se encuentra el atributo) para evitar que se produzca una excepción.

Otros usos de findgeneralmente se reemplazan con cosas como esta:

Model.all
Model.first

find_byse usa como ayuda cuando se busca información dentro de una columna, y se asigna a tal con las convenciones de nomenclatura. Por ejemplo, si tiene una columna nombrada nameen su base de datos, usaría la siguiente sintaxis:

Model.find_by(name: "Bob")

.where es más fácil de atrapar todo lo que le permite usar una lógica un poco más compleja para cuando los ayudantes convencionales no lo harán, y devuelve una matriz de elementos que coinciden con sus condiciones (o una matriz vacía de lo contrario).

Juan
fuente
62
find_byno está en desuso, pero la sintaxis está cambiando un poco. De find_by_name("Bob")a find_by(:name, "Bob").
Brian Morearty
61
@BrianMorearty, creo que querías decirfind_by(name: "Bob")
MCB
1
@BrianMorearty No pude encontrar ninguna evidencia de find_by_...estar en desuso, ¿tiene una fuente? Parece find_byy find_by_...ambos siguen siendo compatibles con Rails 4.
Dennis
44
@ Dennis, tienes razón en que no está en desuso, y eso es lo que dije. Pero podría haber sido más claro cuando dije "pero la sintaxis está cambiando un poco". Lo que quise decir fue: "pero ahora también hay disponible una nueva sintaxis". Vea la corrección de MCB para la nueva sintaxis.
Brian Morearty el
3
Esto es lo que se menciona en la versión de rails 4.0 "Todos los métodos dinámicos excepto find_by _... y find_by _...! Están en desuso" más detalles de edgeguides.rubyonrails.org/…
Mukesh Singh Rathaur
131

donde devuelve ActiveRecord :: Relación

Ahora eche un vistazo a la implementación find_by:

def find_by
  where(*args).take
end

Como puede ver, find_by es igual que where pero solo devuelve un registro. Este método debe usarse para obtener 1 registro y dónde debe usarse para obtener todos los registros con algunas condiciones.

Mike Andrianov
fuente
1
find_by devuelve un objeto, pero donde devuelve una colección.
Kick Buttowski
Cuando consulta valor fuera de rango, find_byrescatará ::RangeErrora partir where(*args) y nula rentabilidad.
Fangxing
34

Model.find

1- Parámetro: ID del objeto a buscar.

2- Si se encuentra: devuelve el objeto (solo un objeto).

3- Si no se encuentra: plantea una ActiveRecord::RecordNotFoundexcepción.

Model.find_by

1- Parámetro: clave / valor

Ejemplo:

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

2- Si se encuentra: Devuelve el objeto.

3- Si no se encuentra: devuelve nil.

Nota: si quieres que aumente elActiveRecord::RecordNotFoundusofind_by!

Model.where

1- Parámetro: igual que find_by

2- Si se encuentra: devuelve que ActiveRecord::Relationcontiene uno o más registros que coinciden con los parámetros.

3- Si no se encuentra: Devuelve un Vacío ActiveRecord::Relation.

Hossam Khamis
fuente
31

Hay una diferencia entre findy find_byque finddevolverá un error si no se encuentra, mientras find_byque devolverá nulo.

A veces es más fácil de leer si tiene un método como find_by email: "haha", en lugar de .where(email: some_params).first.

Kasumi
fuente
17

Desde Rails 4 puedes hacer:

User.find_by(name: 'Bob')

que es el equivalente find_by_nameen Rails 3.

Usar #wherecuando #findy #find_byno son suficientes.

Agis
fuente
2
Agis, estoy de acuerdo con usted, pero he estado buscando en Internet por qué deberíamos usarlo find_byy no find_by_<column_name>. Lo necesito para responder a alguien.
KULKING
Agis, ¿tiene alguna fuente para respaldar su afirmación de que ya no deberíamos usar find_by_nameen Rails 4? Que yo sepa, no ha quedado en desuso .
Dennis
¿Hay una manera simple de hacer eso, pero decir que el nombre tiene un comodín en ella así que algo comofind_by(name: "Rob*")
Batman
1
@Dennis Es posible usar ambos, son válidos. Prefiero la nueva sintaxis ya que la API es más intuitiva en mi humilde opinión. Así es como lo diseñaría yo mismo :)
Agis
8

La respuesta aceptada generalmente lo cubre todo, pero me gustaría agregar algo, solo en caso de que esté planeando trabajar con el modelo de una manera similar a la actualización, y esté recuperando un solo registro ( iddel cual no sabe), entonces find_byes el camino a seguir, porque recupera el registro y no lo coloca en una matriz

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

pero si lo usas, whereentonces no puedes actualizarlo directamente

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)

en ese caso deberías especificarlo así

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
fuente
exactamente lo que estaba buscando
Paul Brunache
2
@kit = Kit.where (número: "3456"). Primero - Esto le permite actualizarlo directamente y es más seguro ya que sobrevivió a la depreciación
Paul Brunache
6

Además de la respuesta aceptada, lo siguiente también es válido

Model.find()puede aceptar una matriz de identificadores y devolverá todos los registros que coincidan. Model.find_by_id(123)también acepta matriz pero solo procesará el primer valor de identificación presente en la matriz

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

Las respuestas dadas hasta ahora están bien.

Sin embargo, una diferencia interesante es que las Model.findbúsquedas por id; si se encuentra, devuelve un Modelobjeto (solo un registro) pero arroja un objeto diferente ActiveRecord::RecordNotFound.

Model.find_byes muy similar Model.findy le permite buscar cualquier columna o grupo de columnas en su base de datos, pero devuelve nilsi ningún registro coincide con la búsqueda.

Model.wherePor otro lado, devuelve un Model::ActiveRecord_Relationobjeto que es como una matriz que contiene todos los registros que coinciden con la búsqueda . Si no se encuentra ningún registro, devuelve un Model::ActiveRecord_Relationobjeto vacío .

Espero que esto te ayude a decidir cuál usar en cualquier momento.

escalón
fuente
3

Supongamos que tengo un modelo User

  1. User.find(id)

Devuelve una fila donde la clave primaria = id. El tipo de retorno será Userobjeto.

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

Devuelve la primera fila con el atributo correspondiente o correo electrónico en este caso. El tipo de retorno será Userobjeto nuevamente.

Nota: User.find_by(email: "[email protected]")es similar aUser.find_by_email("[email protected]")

  1. User.where(project_id:1)

Devuelve todos los usuarios en la tabla de usuarios donde el atributo coincide.

Aquí el tipo de retorno será ActiveRecord::Relationobjeto. ActiveRecord::RelationLa clase incluye el Enumerablemódulo de Ruby para que pueda usar su objeto como una matriz y recorrerlo.

Nikhil Mohadikar
fuente
0

La mejor parte de trabajar con cualquier tecnología de código abierto es que puede inspeccionar su longitud y amplitud. Mira este enlace

find_by ~> Encuentra el primer registro que coincide con las condiciones especificadas. No existe un pedido implícito, por lo que si el pedido es importante, debe especificarlo usted mismo. Si no se encuentra ningún registro, devuelve nulo.

find ~> Encuentra el primer registro que coincide con las condiciones especificadas, pero si no se encuentra ningún registro, genera una excepción, pero eso se hace deliberadamente.

Verifique el enlace anterior, tiene todas las explicaciones y casos de uso para las siguientes dos funciones.

NIshank
fuente
-5

Yo personalmente recomendaré usar

where(< columnname> => < columnvalue>)
Prateek Alakh
fuente
1
Esto puede responder la pregunta. Pero intente describir los pros y los contras de usar su enfoque, en lugar de la respuesta aceptada.
Vivek Kumar