find vs find_by vs where


127

Sono nuovo alle rotaie. Quello che vedo ci sono molti modi per trovare un disco:

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

E sembra che tutti finiscano per generare esattamente lo stesso SQL. Inoltre, credo che lo stesso sia vero per trovare più record:

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

Esiste una regola empirica o una raccomandazione su quale utilizzare?

Risposte:


103

Usa quello che ritieni più adatto alle tue esigenze.

Il findmetodo viene generalmente utilizzato per recuperare una riga in base all'ID:

Model.find(1)

Vale la pena notare che findgenererà un'eccezione se l'articolo non viene trovato dall'attributo fornito. Utilizzare where(come descritto di seguito, che restituirà un array vuoto se l'attributo non viene trovato) per evitare che venga generata un'eccezione.

Altri usi di findsolito vengono sostituiti con cose come questa:

Model.all
Model.first

find_byviene utilizzato come aiuto quando si cercano informazioni all'interno di una colonna e si associa a tale con le convenzioni di denominazione. Ad esempio, se hai una colonna denominata namenel tuo database, utilizzeresti la sintassi seguente:

Model.find_by(name: "Bob")

.where è più di una cattura tutto ciò che ti consente di utilizzare una logica un po 'più complessa per quando gli helper convenzionali non lo faranno, e restituisce una matrice di elementi che corrispondono alle tue condizioni (o una matrice vuota altrimenti).


62
find_bynon è obsoleto, ma la sintassi sta cambiando un po '. Da find_by_name("Bob")a find_by(:name, "Bob").
Brian Morearty,

61
@BrianMorearty Credo che volevi direfind_by(name: "Bob")
MCB

1
@BrianMorearty Non sono riuscito a trovare alcuna prova di find_by_...essere deprecato, hai una fonte? Sembra find_bye find_by_...sono ancora entrambi supportati in Rails 4.
Dennis

4
@Dennis, hai ragione sul fatto che non è deprecato, ed è quello che ho detto. Ma avrei potuto essere più chiaro quando ho detto "ma la sintassi sta cambiando un po '". Quello che volevo dire era "ma ora è disponibile anche una nuova sintassi". Vedi la correzione di MCB per la nuova sintassi.
Brian Morearty,

3
Questo è quanto menzionato nella versione 4.0 di rails "Tutti i metodi dinamici tranne find_by _... e find_by _...! Sono deprecati" maggiori dettagli su edgeguides.rubyonrails.org/…
Mukesh Singh Rathaur

131

dove restituisce ActiveRecord :: Relation

Ora dai un'occhiata all'implementazione di find_by:

def find_by
  where(*args).take
end

Come puoi vedere find_by è lo stesso di dove ma restituisce solo un record. Questo metodo dovrebbe essere usato per ottenere 1 record e dove dovrebbe essere usato per ottenere tutti i record con alcune condizioni.


1
find_by restituisce un oggetto, ma dove restituisce una raccolta.
Calcia Buttowski il

Quando interrogazione valore fuori campo, find_bysalverà ::RangeErrorda where(*args) e nil ritorno.
fangxing

34

Model.find

1- Parametro: ID dell'oggetto da trovare.

2- Se trovato: restituisce l'oggetto (solo un oggetto).

3- Se non trovato: genera ActiveRecord::RecordNotFoundun'eccezione.

Model.find_by

1- Parametro: chiave / valore

Esempio:

User.find_by name: 'John', email: 'john@doe.com'

2- Se trovato: restituisce l'oggetto.

3- Se non trovato: ritorna nil.

Nota: se si desidera aumentare l'ActiveRecord::RecordNotFoundutilizzofind_by!

Model.where

1- Parametro: uguale a find_by

2- Se trovato: restituisce ActiveRecord::Relationcontenente uno o più record corrispondenti ai parametri.

3- Se non trovato: restituisce un vuoto ActiveRecord::Relation.


31

C'è una differenza tra finde find_byin che findrestituirà un errore se non trovato, mentre find_byrestituirà null.

A volte è più facile leggere se hai un metodo simile find_by email: "haha", al contrario di .where(email: some_params).first.


17

Da Rails 4 puoi fare:

User.find_by(name: 'Bob')

che è l'equivalente find_by_namein Rails 3.

Utilizzare #wherequando #finde #find_bynon sono sufficienti.


2
Agis, sono d'accordo con te, ma ho cercato su Internet il motivo per cui dovremmo usare find_bye non find_by_<column_name>. Ne ho bisogno per rispondere a qualcuno.
KULKING,

Agis, hai qualche fonte a sostegno del tuo reclamo che non dovremmo più utilizzare find_by_namein Rails 4? Per quanto ne so, non è stato deprecato .
Dennis,

C'è un modo semplice per farlo ma dire che il nome ha un carattere jolly al suo interno, quindi qualcosa del generefind_by(name: "Rob*")
Batman,

1
@Dennis È possibile utilizzare entrambi, sono validi. Preferisco la nuova sintassi poiché l'API è IMHO più intuitiva. È come lo progetterei io stesso :)
Agis,

8

La risposta accettata generalmente copre tutto, ma mi piacerebbe aggiungere qualcosa, nel caso in cui tu stia pianificando di lavorare con il modello in un modo come l'aggiornamento, e stai recuperando un singolo record (di cui idnon conosci), quindi find_byè la strada da percorrere, perché recupera il record e non lo inserisce in un array

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

ma se lo usi wherenon puoi aggiornarlo direttamente

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)

in tal caso dovresti specificarlo in questo modo

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

esattamente quello che stavo cercando
Paul Brunache il

2
@kit = Kit.where (numero: "3456"). In primo luogo - Questo ti consente di aggiornarlo direttamente ed è più sicuro poiché è sopravvissuto alla deprecazione
Paul Brunache,

6

Oltre alla risposta accettata, vale anche quanto segue

Model.find()può accettare un array di ID e restituirà tutti i record corrispondenti. Model.find_by_id(123)accetta anche array ma elaborerà solo il primo valore ID presente nell'array

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


3

Le risposte fornite finora sono tutte OK.

Tuttavia, una differenza interessante è che Model.findcerca per ID; se trovato, restituisce un Modeloggetto (solo un singolo record) ma genera un ActiveRecord::RecordNotFoundaltro.

Model.find_byè molto simile Model.finde ti consente di cercare qualsiasi colonna o gruppo di colonne nel tuo database ma restituisce nilse nessun record corrisponde alla ricerca.

Model.whered'altra parte restituisce un Model::ActiveRecord_Relationoggetto che è proprio come un array contenente tutti i record che corrispondono alla ricerca . Se non viene trovato alcun record, restituisce un Model::ActiveRecord_Relationoggetto vuoto .

Spero che questi ti aiutino a decidere quale utilizzare in qualsiasi momento.


3

Supponiamo che io abbia un modello User

  1. User.find(id)

Restituisce una riga in cui chiave primaria = id. Il tipo restituito sarà Useroggetto.

  1. User.find_by(email:"abc@xyz.com")

Restituisce la prima riga con l'attributo corrispondente o l'e-mail in questo caso. Il tipo restituito sarà di Usernuovo oggetto.

Nota: - User.find_by(email: "abc@xyz.com")è simile aUser.find_by_email("abc@xyz.com")

  1. User.where(project_id:1)

Restituisce tutti gli utenti nella tabella degli utenti in cui corrisponde l'attributo.

Qui il tipo restituito sarà ActiveRecord::Relationoggetto. ActiveRecord::RelationLa classe include il Enumerablemodulo di Ruby in modo che tu possa usare il suo oggetto come un array e attraversarlo.


0

La parte migliore di lavorare con qualsiasi tecnologia open source è che puoi controllarne la lunghezza e l'ampiezza. Dai un'occhiata a questo link

find_by ~> Trova il primo record corrispondente alle condizioni specificate. Non esiste un ordine implicito, quindi se l'ordine conta, dovresti specificarlo tu stesso. Se non viene trovato alcun record, restituisce zero.

find ~> Trova il primo record corrispondente alle condizioni specificate, ma se non viene trovato alcun record, genera un'eccezione, ma ciò viene fatto deliberatamente.

Controlla il link sopra, ha tutte le spiegazioni e i casi d'uso per le seguenti due funzioni.


-5

Raccomanderò personalmente l'uso

where(< columnname> => < columnvalue>)

1
Questo potrebbe rispondere alla domanda. Ma prova a descrivere i pro e i contro dell'utilizzo del tuo approccio, anziché della risposta accettata.
Vivek Kumar,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.