find vs find_by vs where

127

Eu sou novo nos trilhos. O que vejo é que existem várias maneiras de encontrar um registro:

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

E parece que todos eles acabam gerando exatamente o mesmo SQL. Além disso, acredito que o mesmo é verdade para encontrar vários registros:

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

Existe uma regra de ouro ou recomendação sobre qual usar?

Victor Ronin
fonte

Respostas:

103

Use o que achar mais adequado às suas necessidades.

O findmétodo geralmente é usado para recuperar uma linha por ID:

Model.find(1)

Vale a pena notar que isso findgerará uma exceção se o item não for encontrado pelo atributo que você fornece. Use where(como descrito abaixo, que retornará uma matriz vazia se o atributo não for encontrado) para evitar que uma exceção seja lançada.

Outros usos de findgeralmente são substituídos por coisas como esta:

Model.all
Model.first

find_byé usado como auxiliar quando você procura informações em uma coluna e é mapeado para isso com convenções de nomenclatura. Por exemplo, se você tiver uma coluna nomeada nameem seu banco de dados, usaria a seguinte sintaxe:

Model.find_by(name: "Bob")

.where é mais um problema, o que permite que você use uma lógica um pouco mais complexa para quando os auxiliares convencionais não o fazem, e retorna uma matriz de itens que correspondem às suas condições (ou uma matriz vazia de outra forma).

John
fonte
62
find_bynão está obsoleto, mas a sintaxe está mudando um pouco. De find_by_name("Bob")para find_by(:name, "Bob").
Brian Morearty 31/03
61
@BrianMorearty Eu acredito que você quis dizerfind_by(name: "Bob")
MCB /
1
@BrianMorearty Não consegui encontrar nenhuma evidência de find_by_...descontinuação, você tem uma fonte? Parece find_bye find_by_...são ambos ainda apoiado no Rails 4.
Dennis
4
@ Dennis, você está certo de que não foi preterido, e foi o que eu disse. Mas eu poderia ter sido mais claro quando disse "mas a sintaxe está mudando um pouco". O que eu quis dizer foi ", mas uma nova sintaxe também está disponível agora". Veja a correção do MCB para a nova sintaxe.
Brian Morearty
3
Isto é o que mencionado na trilhos versão 4.0 "Todos os métodos dinâmicos, exceto para find_by _... e find_by _... estão obsoletas!" Mais detalhes do edgeguides.rubyonrails.org/...
Mukesh Singh Rathaur
131

onde retorna ActiveRecord :: Relation

Agora, dê uma olhada na implementação find_by:

def find_by
  where(*args).take
end

Como você pode ver, find_by é o mesmo que where, mas retorna apenas um registro. Este método deve ser usado para obter 1 registro e onde deve ser usado para obter todos os registros com algumas condições.

Mike Andrianov
fonte
1
find_by retorna um objeto, mas retorna uma coleção.
pontapé Buttowski
Quando consulta valor fora da faixa, find_byirá resgatar ::RangeErrorde where(*args) e retorno nulo.
Fangxing
34

Model.find

1- Parâmetro: ID do objeto a ser encontrado.

2- Se encontrado: retorna o objeto (apenas um objeto).

3- Se não encontrado: gera uma ActiveRecord::RecordNotFoundexceção.

Model.find_by

1- Parâmetro: chave / valor

Exemplo:

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

2- Se encontrado: retorna o objeto.

3- Se não encontrado: retorna nil.

Nota: Se você deseja aumentar oActiveRecord::RecordNotFoundusofind_by!

Model.where

1- Parâmetro: o mesmo que find_by

2- Se encontrado: retorna ActiveRecord::Relationcontendo um ou mais registros correspondentes aos parâmetros.

3- Se não for encontrado: Retorna um Empty ActiveRecord::Relation.

Hossam Khamis
fonte
31

Há uma diferença entre finde find_byque findretornará um erro se não for encontrado, enquanto find_byretornará nulo.

Às vezes, é mais fácil ler se você tiver um método como find_by email: "haha", ao contrário de .where(email: some_params).first.

Kasumi
fonte
17

Desde o Rails 4, você pode:

User.find_by(name: 'Bob')

que é o equivalente find_by_nameno Rails 3.

Use #wherequando #finde #find_bynão é suficiente.

Agis
fonte
2
Agis, eu concordo com você, mas eu tenho pesquisado na internet que por que devemos usar find_bye não find_by_<column_name>. Eu preciso responder a alguém.
KULKING
Agis, você tem alguma fonte para fazer backup de sua reivindicação que não devemos mais usar find_by_nameno Rails 4? Até onde eu sei, não foi preterido .
Dennis
Existe uma maneira simples de fazer isso, mas digamos que o nome tenha um curinga, algo comofind_by(name: "Rob*")
Batman
1
@ Dennis É possível usar os dois, eles são válidos. Eu prefiro a nova sintaxe, pois a API é IMHO mais intuitiva. É assim que eu iria projetá-lo:)
Agis
8

A resposta aceita geralmente cobre tudo, mas eu gostaria de acrescentar algo, basta meter você está planejando para trabalhar com o modelo de uma forma como a actualização, e você está recuperando um único registro (cujo idvocê não sabe), então find_byé o caminho a percorrer, porque recupera o registro e não o coloca em uma 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

mas se você usar where, não poderá atualizá-lo diretamente

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)

nesse caso, você teria que especificá-lo assim

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
fonte
exatamente o que eu estava procurando
Paul Brunache
2
@kit = Kit.where (número: "3456") em primeiro lugar - Isso permite que você atualizá-lo directamente e que é mais seguro, uma vez que sobreviveu a depreciação.
Paul Brunache
6

Além da resposta aceita, o seguinte também é válido

Model.find()pode aceitar uma matriz de IDs e retornará todos os registros correspondentes. Model.find_by_id(123)também aceita matriz, mas processará apenas o primeiro valor de ID presente na matriz

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

As respostas dadas até agora estão bem.

No entanto, uma diferença interessante é que as Model.findpesquisas por ID; se encontrado, ele retorna um Modelobjeto (apenas um único registro), mas lança um ActiveRecord::RecordNotFoundcontrário.

Model.find_byé muito semelhante Model.finde permite pesquisar qualquer coluna ou grupo de colunas no banco de dados, mas retorna nilse nenhum registro corresponder à pesquisa.

Model.wherepor outro lado, retorna um Model::ActiveRecord_Relationobjeto que é exatamente como uma matriz que contém todos os registros que correspondem à pesquisa . Se nenhum registro foi encontrado, ele retornará um Model::ActiveRecord_Relationobjeto vazio .

Espero que eles o ajudem a decidir qual usar a qualquer momento.

escalão
fonte
3

Suponha que eu tenho um modelo User

  1. User.find(id)

Retorna uma linha onde chave primária = id. O tipo de retorno será Userobjeto.

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

Retorna a primeira linha com atributo ou email correspondente nesse caso. O tipo de retorno será Userobjeto novamente.

Nota: - User.find_by(email: "[email protected]")é semelhante aUser.find_by_email("[email protected]")

  1. User.where(project_id:1)

Retorna todos os usuários na tabela de usuários em que o atributo corresponde.

Aqui o tipo de retorno será ActiveRecord::Relationobjeto. ActiveRecord::RelationA classe inclui o Enumerablemódulo Ruby para que você possa usá-lo como uma matriz e percorrer nela.

Nikhil Mohadikar
fonte
0

A melhor parte de trabalhar com qualquer tecnologia de código aberto é que você pode inspecionar o comprimento e a largura. Confira este link

find_by ~> Encontra o primeiro registro que corresponde às condições especificadas. Não há pedidos implícitos; portanto, se o pedido for importante, você mesmo deve especificar. Se nenhum registro for encontrado, retornará nulo.

find ~> Encontra o primeiro registro que corresponde às condições especificadas, mas se nenhum registro for encontrado, isso gera uma exceção, mas isso é feito deliberadamente.

Faça o checkout no link acima, ele tem todas as explicações e casos de uso para as duas funções a seguir.

NIshank
fonte
-5

Eu recomendarei pessoalmente o uso

where(< columnname> => < columnvalue>)
Prateek Alakh
fonte
1
Isso pode responder à pergunta. Mas tente descrever os prós e os contras de usar sua abordagem, em vez da resposta aceita.
Vivek Kumar