znajdź vs find_by vs gdzie

131

Jestem nowy w railach. Co widzę, jest wiele sposobów na znalezienie rekordu:

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

Wygląda na to, że wszystkie z nich generują dokładnie ten sam kod SQL. Uważam też, że to samo dotyczy znajdowania wielu rekordów:

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

Czy istnieje praktyczna zasada lub zalecenie, którego z nich należy użyć?

Victor Ronin
źródło

Odpowiedzi:

106

Użyj tego, który najlepiej odpowiada Twoim potrzebom.

Ta findmetoda jest zwykle używana do pobierania wiersza według identyfikatora:

Model.find(1)

Warto zauważyć, że findzgłosi wyjątek, jeśli przedmiot nie zostanie znaleziony przez dostarczony atrybut. Użyj where(zgodnie z opisem poniżej, co zwróci pustą tablicę, jeśli atrybut nie zostanie znaleziony), aby uniknąć zgłaszania wyjątku.

Inne zastosowania findsą zwykle zastępowane takimi rzeczami:

Model.all
Model.first

find_byjest używany jako pomocnik podczas wyszukiwania informacji w kolumnie i odwzorowuje na takie przy użyciu konwencji nazewnictwa. Na przykład, jeśli masz kolumnę o nazwie namew swojej bazie danych, użyjesz następującej składni:

Model.find_by(name: "Bob")

.where jest bardziej chwytem, ​​który pozwala ci użyć nieco bardziej złożonej logiki, gdy konwencjonalni pomocnicy nie zrobią tego, i zwraca tablicę elementów, które pasują do twoich warunków (lub pustą tablicę w przeciwnym razie).

Jan
źródło
62
find_bynie jest przestarzała, ale składnia nieco się zmienia. Od find_by_name("Bob")do find_by(:name, "Bob").
Brian Morearty
61
@BrianMorearty Myślę, że miałeś na myślifind_by(name: "Bob")
MCB
1
@BrianMorearty Nie mogłem znaleźć żadnych dowodów na find_by_...to, że jestem przestarzały, czy masz źródło? Wydaje się, find_byi find_by_...są nadal obsługiwane zarówno w Rails 4.
Dennis
4
@Dennis, masz rację, że to nie jest przestarzałe i to właśnie powiedziałem. Ale mogłem wyrazić się bardziej jasno, kiedy powiedziałem „ale składnia trochę się zmienia”. Chodziło mi o to, że „ale teraz dostępna jest również nowa składnia”. Zobacz poprawkę MCB dla nowej składni.
Brian Morearty
3
To właśnie zostało wspomniane w wydaniu rails 4.0 "Wszystkie metody dynamiczne oprócz find_by _... i find_by _...! Są przestarzałe" więcej szczegółów na stronie edgeguides.rubyonrails.org/…
Mukesh Singh Rathaur
133

gdzie zwraca ActiveRecord :: Relation

Teraz spójrz na implementację find_by:

def find_by
  where(*args).take
end

Jak widać find_by jest tym samym, co gdzie, ale zwraca tylko jeden rekord. Ta metoda powinna być używana do uzyskania 1 rekordu i gdzie powinna być używana do uzyskania wszystkich rekordów z określonymi warunkami.

Mike Andrianov
źródło
1
find_by zwraca obiekt, a gdzie zwraca kolekcję.
Kick Buttowski
Gdy kwerenda wartość poza zakresem, find_byuratuje ::RangeErrorprzed where(*args) i powrót zero.
fangxing
35

Model.find

1- Parametr: identyfikator obiektu do znalezienia.

2- Jeśli znaleziono: zwraca obiekt (tylko jeden obiekt).

3- Jeśli nie zostanie znaleziony: zgłasza ActiveRecord::RecordNotFoundwyjątek.

Model.find_by

1- Parametr: klucz / wartość

Przykład:

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

2- Jeśli znaleziono: zwraca obiekt.

3- Jeśli nie znaleziono: zwraca nil.

Uwaga: jeśli chcesz, aby zwiększyłActiveRecord::RecordNotFoundużyciefind_by!

Model.where

1- Parametr: taki sam jak find_by

2- Jeśli znaleziono: zwraca ActiveRecord::Relationjeden lub więcej rekordów pasujących do parametrów.

3- Jeśli nie znaleziono: zwraca Empty ActiveRecord::Relation.

Hossam Khamis
źródło
31

Istnieje różnica między findi find_byw, findktóra zwróci błąd, jeśli nie zostanie znaleziona, natomiast find_byzwróci wartość null.

Czasami łatwiej jest przeczytać, jeśli masz metodę taką jak find_by email: "haha", w przeciwieństwie do .where(email: some_params).first.

Kasumi
źródło
18

Od Rails 4 możesz:

User.find_by(name: 'Bob')

co jest odpowiednikiem find_by_namew Railsach 3.

Użyj, #wherekiedy #findi #find_bynie wystarczą.

Agis
źródło
2
Agis, zgadzam się z tobą, ale szukałem w Internecie, dlaczego powinniśmy używać, find_bya nie find_by_<column_name>. Potrzebuję tego, żeby komuś odpowiedzieć.
KULKING
Agis, czy masz jakieś źródła potwierdzające twoje twierdzenie, że nie powinniśmy już używać find_by_namew Rails 4? O ile wiem, nie zostało wycofane .
Dennis
Czy jest na to prosty sposób, ale powiedz, że nazwa zawiera symbol wieloznaczny, więc coś w stylufind_by(name: "Rob*")
Batman
1
@Dennis Można używać obu, są one ważne. Wolę nową składnię, ponieważ API jest bardziej intuicyjne w obsłudze IMHO. Tak bym sam to zaprojektował :)
Agis
8

Odpowiedź ogólnie akceptowaną obejmuje to wszystko, ale chciałbym dodać coś, tylko okrywać planuje pracę z modelem w sposób jak aktualizacja, i jesteś pobieranie pojedynczy rekord (którego idnie wiem), to find_byjest droga do zrobienia, ponieważ pobiera rekord i nie umieszcza go w tablicy

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

ale jeśli używasz where, nie możesz go bezpośrednio zaktualizować

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)

w takim przypadku należałoby to określić w ten sposób

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
źródło
dokładnie to, czego szukałem
Paul Brunache
2
@kit = Kit.where (number: "3456"). first - To pozwala na bezpośrednią aktualizację i jest bezpieczniejsze, ponieważ przetrwał wycofanie
Paul Brunache
6

Oprócz zaakceptowanej odpowiedzi ważne jest również następujące

Model.find()może akceptować tablicę identyfikatorów i zwróci wszystkie zgodne rekordy. Model.find_by_id(123)akceptuje również tablicę, ale przetwarza tylko pierwszą wartość id obecną w tablicy

Model.find([1,2,3])
Model.find_by_id([1,2,3])
Saumya Mehta
źródło
4

Wszystkie dotychczas udzielone odpowiedzi są OK.

Jednak jedną interesującą różnicą jest to, że Model.findwyszukiwanie według identyfikatora; jeśli zostanie znaleziony, zwraca Modelobiekt (tylko pojedynczy rekord), ale zgłasza ActiveRecord::RecordNotFoundinaczej.

Model.find_byjest bardzo podobny do Model.findi umożliwia przeszukiwanie dowolnej kolumny lub grupy kolumn w bazie danych, ale zwraca, niljeśli żaden rekord nie pasuje do wyszukiwania.

Model.wherez drugiej strony zwraca Model::ActiveRecord_Relationobiekt, który przypomina tablicę zawierającą wszystkie rekordy pasujące do wyszukiwania . Jeśli nie znaleziono rekordu, zwraca pusty Model::ActiveRecord_Relationobiekt.

Mam nadzieję, że pomogą ci one zdecydować, którego użyć w dowolnym momencie.

rzut
źródło
3

Załóżmy, że mam model User

  1. User.find(id)

Zwraca wiersz, w którym klucz podstawowy = id. Zwracanym typem będzie Userobiekt.

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

W tym przypadku zwraca pierwszy wiersz z pasującym atrybutem lub adresem e-mail. Typ zwracany będzie Userponownie obiektem.

Uwaga: - User.find_by(email: "[email protected]")jest podobny doUser.find_by_email("[email protected]")

  1. User.where(project_id:1)

Zwraca wszystkich użytkowników w tabeli użytkowników, w której atrybut jest zgodny.

Tutaj typem zwracanym będzie ActiveRecord::Relationobiekt. ActiveRecord::Relationclass zawiera Enumerablemoduł Rubiego, więc możesz używać jego obiektu jako tablicy i przechodzić po niej.

Nikhil Mohadikar
źródło
0

Najlepszą częścią pracy z dowolną technologią open source jest to, że można sprawdzić jej długość i szerokość. Sprawdź ten link

find_by ~> Znajduje pierwszy rekord spełniający określone warunki. Nie ma dorozumianych zamówień, więc jeśli zamówienie ma znaczenie, należy je określić samodzielnie. Jeśli żaden rekord nie zostanie znaleziony, zwraca nil.

find ~> Znajduje pierwszy rekord spełniający określone warunki, ale jeśli nie zostanie znaleziony żaden rekord, zgłasza wyjątek, ale jest to wykonywane celowo.

Sprawdź powyższy link, zawiera wszystkie wyjaśnienia i przypadki użycia dla następujących dwóch funkcji.

NIshank
źródło
-5

Osobiście polecam używanie

where(< columnname> => < columnvalue>)
Prateek Alakh
źródło
1
To może odpowiedzieć na pytanie. Spróbuj jednak opisać zalety i wady korzystania z własnego podejścia zamiast akceptowanej odpowiedzi.
Vivek Kumar