2013-08-06 10 views
38

Jaki jest najskuteczniejszy sposób sprawdzenia, czy baza danych zwróci rekord przed przetworzeniem. Przykład: Truck.where("id = ?", id).select('truck_no').first.truck_noSzyny sprawdzające, czy rekord istnieje w bazie danych

To może, ale nie musi, wrócić do ciężarówki, jeśli ciężarówka istnieje. Jaki jest najskuteczniejszy sposób zapewnienia, że ​​strona nie zawiedzie się podczas przetwarzania tego żądania. Jak poradziłbym sobie z tym zarówno w widoku, jak i kontrolerem, powiedzmy, że używałem pętli, aby przejść przez każdą ciężarówkę i wydrukować jej numer.

Jeśli rekord nie istnieje, chciałbym mieć możliwość wydrukowania komunikatu zamiast stwierdzenia, że ​​nie znaleziono rekordów.

+0

która wersja szynach jesteś stronie? – Shaunak

+2

Czy istnieje przegląd?metoda http://api.rubyonrails.org/classes/ActiveRecord/FinderMethods.html#method-i-exists-3F – cristian

Odpowiedz

82

Jeśli chcesz sprawdzić, czy obiekt istnieje, dlaczego nie użyć?

if Truck.exists?(10) 
    # your truck exists in the database 
else 
    # the truck doesn't exists 
end 

Sposób exists? ma tę zaletę, że nie jest wybór rekordu z bazy danych (czyli jest szybszy niż wybierając rekord). Kwerenda wygląda następująco:

SELECT 1 FROM trucks where trucks.id = 10 

Można znaleźć więcej przykładów na http://api.rubyonrails.org/classes/ActiveRecord/FinderMethods.html#method-i-exists-3F

36

Oto jak możesz to sprawdzić.

if Trucks.where(:id => current_truck.id).blank? 
    # no truck record for this id 
else 
    # at least 1 record for this truck 
end 

gdzie metoda zwraca obiekt ActiveRecord :: Relacja (działa jak tablica, która zawiera wyniki gdzie), to może być pusty, ale nigdy nie może być zerowa.

+0

Zawsze myślałem, że zwróci zero. Dziękuję za wyjaśnienie tego. – Bojan

+0

@ ktokolwiek tylko przegłosował to. Dodaj komentarz, aby pomóc zrozumieć, co tu jest nie tak. – Shaunak

+5

Użyj 'exist?' Insetad of 'blank? '. Spowoduje to wykonanie zapytania w celu sprawdzenia, czy obiekt istnieje w bazie danych zamiast pobierania całego obiektu. –

1

Można po prostu zrobić:

@truck_no = Truck.where("id = ?", id).pluck(:truck_no).first 

ten powróci nil jeśli nie znaleziono rekordu lub truck_no z tylko pierwszy rekord inaczej.

Następnie w widoku można po prostu zrobić coś takiego:

<%= @truck_no || "There are no truck numbers" %> 

Jeśli chcesz pobierać i wyświetlać wiele wyników, a następnie w kontrolerze:

@truck_nos = Truck.where("id = ?", id).pluck(:truck_no) 

iw widoku:

<% truck_nos.each do |truck_no| %> 
    <%= truck_no %> 
<% end %> 

<%= "No truck numbers to iterate" if truck_nos.blank? %> 
+0

Dziękuję za sugestię. Nigdy nie wiedziałem o metodzie skubania. – Bojan

+0

Nie ma za co. 'pluck' wybierze pojedynczą kolumnę ze swojej tabeli. – Agis

8

PO rzeczywisty przypadek użycia roztworu

Najprostszym rozwiązaniem jest łączenie czek i odzyskiwanie danych DB w 1 Zapytanie DB zamiast oddzielnych wywołań DB. Twój przykładowy kod jest bliski i przekazuje twoje intencje, ale jest trochę poza rzeczywistą składnią.

Jeśli proste zrobić Truck.where("id = ?", id).select('truck_no').first.truck_no i ten rekord nie istnieje, będzie rzucać się błąd nil kiedy zadzwonić truck_no ponieważ first może pobrać nil rekord, jeśli nie odnajdzie pasujących do Twoich kryteriów.

Dzieje się tak, ponieważ zapytanie zwróci tablicę obiektów spełniających kryteria, a następnie wykona się first w tej tablicy, która (jeśli nie znaleziono pasujących rekordów) to nil.

Dość czyste rozwiązanie:

# Note: using Rails 4/Ruby 2 syntax 
first_truck = Truck.select(:truck_no).find_by(id) # => <Truck id: nil, truck_no: "123"> OR nil if no record matches criteria 

if first_truck 
    truck_number = first_truck.truck_no 
    # do some processing... 
else 
    # record does not exist with that criteria 
end 

polecam czystą składnię, że „komentarze” tak samo inni wiedzą dokładnie, co chcesz robić.

Jeśli naprawdę chcesz, aby przejść dodatkowe mile, można dodać metodę do klasy Truck że robi to dla ciebie i przekazuje swój zamiar:

# truck.rb model 
class Truck < ActiveRecord::Base 
    def self.truck_number_if_exists(record_id) 
    record = Truck.select(:truck_no).find_by(record_id) 
    if record 
     record.truck_no 
    else 
     nil # explicit nil so other developers know exactly what's going on 
    end 
    end 
end 

Wtedy to nazwać tak:

if truck_number = Truck.truck_number_if_exists(id) 
    # do processing because record exists and you have the value 
else 
    # no matching criteria 
end 

Metoda ActiveRecord.find_by pobierze pierwszy rekord, który odpowiada Twoim kryteriom lub zwróci nil, jeśli nie zostanie znaleziony żaden rekord z tymi kryteriami. Zauważ, że kolejność metod find_by i where jest ważna; musisz zadzwonić pod numer select w modelu Truck. Dzieje się tak, ponieważ po wywołaniu metody where faktycznie zwracasz obiekt ActiveRelation, który nie jest tym, czego szukasz.

See ActiveRecord API for 'find_by' method

rozwiązania ogólne używając 'istnieje? metoda

Jak już wspomnieli niektórzy z innych autorów, metoda exists? została zaprojektowana specjalnie w celu sprawdzenia, czy coś istnieje. Nie zwraca wartości, tylko potwierdza, że ​​DB ma rekord, który pasuje do niektórych kryteriów.

Jest to przydatne, jeśli trzeba zweryfikować unikalność lub dokładność niektórych danych. Dobrą rzeczą jest to, że pozwala ci użyć kryteriów ActiveRelation(Record?) where(...).

Na przykład, jeśli masz User model z atrybutem email i trzeba sprawdzić, czy e-mail już istnieje w dB:

User.exists?(email: "[email protected]")

Zaletą korzystania exists? to, że zapytanie SQL prowadzony jest

SELECT 1 AS one FROM "users" WHERE "users"."email" = '[email protected]' LIMIT 1

który jest bardziej wydajny niż faktycznie przekazujących dane.

Jeśli potrzebujesz rzeczywiście warunkowo pobrać dane z DB, nie jest to metoda użycia. Jednak sprawdza się doskonale przy prostym sprawdzaniu, a składnia jest bardzo jasna, więc inni deweloperzy dokładnie wiedzą, co robisz. Używanie odpowiedniej składni ma kluczowe znaczenie w projektach z wieloma programistami. Napisz czysty kod i pozwól, aby kod sam się "skomentował".

+2

to bardzo pomocna instrukcja, dziękuję –

+0

W rubinie każda funkcja zwracająca true lub false, która jest "truck_number_if_exists (id)" w twoim przypadku, powinna mieć znak zapytania na końcu – ImranNaqvi

+0

@ImranNaqvi: masz rację, że konwencja w Ruby ma dołączać '?' Do metod boolowskich; jednak w tym przypadku 'truck_number_if_exist (id)' nie jest metodą boolowską; zwraca wartość, jeśli istnieje, lub zwraca zero. Ponieważ nie jest to metoda boolowska, nie powinna używać znaku "?" Na końcu. –

Powiązane problemy