find () com nil quando não houver registros

95

No meu programa Rails atual, quando uso algo como

 user = User.find(10)

Quando não houver nenhum usuário com ID = 10, terei exceção como:

ActiveRecord::RecordNotFound: Couldn't find User with ID=10

Posso obter nil em vez de gerar exceção, então quando faço algo como:

unless user = Challenge.find(10)
  puts "some error msg"         
end

Só quero obter nil quando não houver registros e não quiser usar begin / rescue

obrigado

Eqbal
fonte
1
possível duplicata de Model.find (1) dá erro ActiveRecord quando id 1 não existe
Tobias Cohen

Respostas:

170

Sim, basta fazer:

Challenge.find_by_id(10)

Para Rails 4 e 5:

Challenge.find_by(id: 10)
apneadiving
fonte
11
ímpar! Eu nunca teria imaginado que o .find_by_*retorno seria nulo e o .findnão retornaria .
ddavison
Isso mudou no rails 4, consulte esta resposta stackoverflow.com/a/26885027/1438478 para a nova maneira de localizar um item por um atributo específico.
Fralcon
Eu encontrei um problema estranho com o Rails 4.2 onde quando você passa um hash como 'x' para Something.find_by(id: x)ele criar uma instrução SQL com todos os pares atributo / valor do hash como parte da cláusula WHERE. Parece um bug do Rails para mim.
Tilo
Rails (3, 4 ou 5) gera localizadores dinâmicos find_by_...para cada atributo que um modelo inclui :id. Portanto, Challenge.find_by_id(10)deve funcionar independentemente da versão do Rails.
Arta
Conforme especificado por @MohamedIbrahim abaixo, você também pode fazer:Challenge.find(10) rescue nil
Hallgeir Wilhelmsen
31

No Rails 4, os localizadores dinâmicos - como os find_by_idusados ​​na resposta aceita - foram descontinuados.

Seguindo em frente, você deve usar a nova sintaxe:

Challenge.find_by id: 10
hattila91
fonte
4
Caso alguém esteja confuso com isso como eu: Challenge.find_by(id: 10)é a outra maneira de escrever isso
Devin Howard
14

você pode fazer isso um pouco hackeado, basta usar a interface de consulta ActiveRecord.

isso retornará nulo, em vez de gerar uma exceção

  User.where(:id => 10).first
gorro
fonte
Uma razão para usar isso find_by_idé que ele é portátil do Rails 3 ao 4. No Rails 4, é find_by(:id => 10).
Gene
5

Por que você simplesmente não captura a exceção? Seu caso se parece exatamente com as exceções feitas para:

begin
  user = User.find(10)
rescue ActiveRecord::RecordNotFound
  puts "some error msg"
end

Se você quiser se recuperar do erro no bloco de resgate (por exemplo, definindo um usuário de espaço reservado (padrão nulo)), você pode continuar com seu código abaixo deste bloco. Caso contrário, você pode simplesmente colocar todo o seu código para o "caso feliz" no bloco entre "começar" e "resgatar".

morgler
fonte
Btw: você nem precisa do begin…endbloco, se você já tem um bloco, como um método de controlador. Nesse caso, a única linha extra de que você precisa é a rescuelinha. Muito mais elegante e fácil de manusear do que verificar nilcom uma ifdeclaração.
Morgler
4

Você pode tentar isso Challenge.exists?(10)

Tonymarschall
fonte
7
será uma solicitação extra de sql
fl00r
Mesmo assim eu acho que é melhor procurar testar
SomeSchmo
use-o se você não se importar com o valor retornado, mas apenas com a presença de um registro no DB
Filip Bartuzi
4

Para aqueles que lutam com mongóide , verifica-se que ambos findefind_by métodos exceções - não importa a versão do Rails!

Há uma opção (ou seja, raise_not_found_error ) que pode ser definida como falsa, mas quando falsey tornafind método, não levanta a exceção também.

Assim, a solução para usuários mongoid é o código nojento:

User.where(id: 'your_id').first # argghhh
Cristiano Mendonça
fonte
O que você acha da solução de resgate nil de mohamed-ibrahim? Parece mais elegante do que .where(email: params[:email]).firstpara mim.
Wylliam Judd
1
Prefiro não usar essa rescuesintaxe, pois ela pode ocultar problemas como erros de digitação
Cristiano Mendonça
Acabei usando a solução de @morgler.
Wylliam Judd
2

tão simples quanto:

user = User.find(10) rescue nil
Mohamed-Ibrahim
fonte
1
Eu preferiria ser mais específico sobre qual erro se espera que seja resgatado, ActiveRecord :: RecordNotFound neste caso. Conforme apontado nesta resposta por @morgler
luizrogeriocn
2
Pessoalmente, considero esta a melhor resposta. Já estou dizendo ao meu código o que fazer se o usuário não for encontrado emif user...else
Wylliam Judd
0

Você pode usar find_by com o atributo necessário (no seu caso, o id). Isso retornará nil ao invés de dar um erro se o id fornecido não for encontrado.

user = Challenge.find_by_id(id_value)

ou você pode usar o novo formato:

user = Challenge.find_by id: id_value

Você também pode usar where, mas você precisa saber que where retorna uma relação de registro ativo com zero ou mais registros que você precisa usar primeiro para retornar apenas um registro ou nil no caso de retorno de zero registros.

user = Challenge.where(id: id_value).first
Alanoud Just
fonte