find () z nil, gdy nie ma rekordów

96

W moim obecnym programie railsowym używam czegoś takiego jak

 user = User.find(10)

Gdy nie ma użytkownika o ID = 10, będę miał wyjątek taki jak:

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

Czy mogę uzyskać zero zamiast zgłaszać wyjątek, więc kiedy robię coś takiego:

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

Chcę tylko uzyskać zero, gdy nie ma rekordów i nie chcę używać rozpoczynania / ratowania

Dzięki

Eqbal
źródło

Odpowiedzi:

172

Tak, po prostu zrób:

Challenge.find_by_id(10)

Dla szyn 4 i 5:

Challenge.find_by(id: 10)
bezdechu
źródło
11
dziwny! Nigdy bym nie przypuszczał, .find_by_*że zwróci zero, a .findnie.
ddavison,
Zmieniło się to w listwach rails 4, zobacz tę odpowiedź stackoverflow.com/a/26885027/1438478, aby poznać nowy sposób znajdowania elementu według określonego atrybutu.
Fralcon
Znalazłem dziwny problem z Railsami 4.2, w którym po przekazaniu do niego skrótu jako „x” Something.find_by(id: x)utworzyłoby się instrukcję SQL ze wszystkimi parami atrybut / wartość hasha jako część klauzuli WHERE. Dla mnie wygląda na błąd w Railsach.
Tilo
Railsy (3, 4 lub 5) generują dynamiczne find_by_...wyszukiwarki dla każdego atrybutu, który zawiera model :id. Challenge.find_by_id(10)Powinien więc działać niezależnie od wersji Railsów.
Arta
Jak określono poniżej w @MohamedIbrahim, możesz również:Challenge.find(10) rescue nil
Hallgeir Wilhelmsen,
31

W Railsach 4, dynamiczne wyszukiwarki - takie jak find_by_idużyte w zaakceptowanej odpowiedzi - zostały wycofane.

Idąc dalej, powinieneś użyć nowej składni:

Challenge.find_by id: 10
hattila91
źródło
4
Na wypadek, gdyby ktoś inny był zdezorientowany, tak jak ja: czy Challenge.find_by(id: 10)jest inny sposób pisania tego
Devin Howard
14

możesz to zrobić trochę hakersko, po prostu użyj interfejsu zapytań ActiveRecord.

zwróci to zero, zamiast zgłosić wyjątek

  User.where(:id => 10).first
czapka bez daszka
źródło
Powodem, dla którego należy tego używać, a nie find_by_iddlatego, że jest przenośny z Rails 3 do 4. W Rails 4 jest find_by(:id => 10).
Gene
5

Dlaczego po prostu nie złapiesz wyjątku? Twoja sprawa wygląda dokładnie tak, jak zostały stworzone wyjątki:

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

Jeśli chcesz naprawić błąd w bloku ratunkowym (np. Ustawiając użytkownika zastępczego (wzorzec null)), możesz kontynuować pracę z kodem poniżej tego bloku. W przeciwnym razie możesz po prostu umieścić cały kod „szczęśliwego przypadku” w bloku między „begin” i „rescue”.

morgler
źródło
Btw: nie potrzebujesz nawet begin…endbloku, jeśli masz już blok, taki jak metoda kontrolera. W takim przypadku jedyną dodatkową linią, której potrzebujesz, jest rescuelinia. O wiele bardziej eleganckie i łatwiejsze w obsłudze niż sprawdzanie za nilpomocą ifoświadczenia.
Morgler
4

Możesz tego spróbować Challenge.exists?(10)

tonymarschall
źródło
7
będzie to dodatkowe żądanie sql
fl00r
Mimo to myślę, że lepiej jest szukać testów
SomeSchmo
używaj go, jeśli nie zależy Ci na zwracanej wartości, a jedynie na obecności rekordu w DB
Filip Bartuzi
4

Dla tych, którzy walczą z mangusty , okazuje się, że obie findi find_bymetody będą stanowić wyjątek - bez względu na wersję railsową !

Istnieje opcja (mianowicie raise_not_found_error ), która może być ustawiona na false, ale gdy falsey sprawia, że findmetoda również nie zgłasza wyjątku.

Tak więc rozwiązaniem dla użytkowników mongoidów jest obrzydliwy kod:

User.where(id: 'your_id').first # argghhh
Cristiano Mendonça
źródło
Co myślisz o rozwiązaniu „Rescue Nil” autorstwa Mohameda-Ibrahima? Wydaje się bardziej eleganckie niż .where(email: params[:email]).firstdla mnie.
Wylliam Judd
1
Wolę nie używać tej rescueskładni, ponieważ może ona ukryć problemy, takie jak literówki
Cristiano Mendonça
Skończyło się na użyciu rozwiązania @ morgler.
Wylliam Judd
2

tak proste, jak:

user = User.find(10) rescue nil
mohamed-ibrahim
źródło
1
Wolałbym bardziej precyzyjnie określić, jaki błąd ma zostać naprawiony, w tym przypadku ActiveRecord :: RecordNotFound. Jak wskazał na tę odpowiedź @morgler
luizrogeriocn
2
Osobiście uważam, że to najlepsza odpowiedź. Już mówię swojemu kodowi, co zrobić, jeśli użytkownika nie maif user...else
Wylliam Judd
0

Możesz użyć find_by z wymaganym atrybutem (w twoim przypadku id), to zwróci nil zamiast dawać błąd, jeśli podany id nie zostanie znaleziony.

user = Challenge.find_by_id(id_value)

lub możesz użyć nowego formatu:

user = Challenge.find_by id: id_value

Możesz również użyć gdzie, ale musisz wiedzieć, że jeśli zwracasz relację rekordu aktywnego z zerem lub większą liczbą rekordów, musisz najpierw użyć, aby zwrócić tylko jeden rekord lub zero w przypadku, gdy nie zwrócą żadnych rekordów.

user = Challenge.where(id: id_value).first
Po prostu głośno
źródło