Domyślna kolejność sortowania dla modelu szyn?

255

Chciałbym określić domyślną kolejność sortowania w moim modelu.

Tak więc, gdy robię a .where()bez określania .order(), używa domyślnego sortowania. Ale jeśli podam an .order(), zastępuje wartość domyślną.

Justin Tanner
źródło

Odpowiedzi:

544

default_scope

Działa to z Railsami 4+:

class Book < ActiveRecord::Base
  default_scope { order(created_at: :desc) }
end

W przypadku Railsów 2.3, 3 potrzebujesz tego:

default_scope order('created_at DESC')

W przypadku szyn 2.x:

default_scope :order => 'created_at DESC'

Gdzie created_atjest pole, w którym ma zostać wykonane domyślne sortowanie.

Uwaga: ASC jest kodem używać do rosnącej i DESC jest malejące ( desc, NIE dsc !).

scope

Gdy już się do tego przyzwyczaisz, możesz także użyć scope:

class Book < ActiveRecord::Base
  scope :confirmed, :conditions => { :confirmed => true }
  scope :published, :conditions => { :published => true }
end

Do Railsów 2 potrzebujesz named_scope.

:publishedzakres daje Book.publishedzamiast Book.find(:published => true).

Od Rails 3 możesz łączyć te metody razem, łącząc je z okresami między nimi, więc dzięki powyższym zakresom możesz teraz używać Book.published.confirmed.

Dzięki tej metodzie zapytanie nie jest faktycznie wykonywane, dopóki nie są potrzebne rzeczywiste wyniki (leniwa ocena), więc 7 zakresów można połączyć razem, ale tylko w wyniku 1 rzeczywistego zapytania do bazy danych, aby uniknąć problemów z wydajnością podczas wykonywania 7 oddzielnych zapytań.

Możesz użyć przekazanego parametru, takiego jak data lub identyfikator_użytkownika (coś, co zmieni się w czasie wykonywania, a więc będzie wymagało „leniwej oceny” z użyciem lambda, takie jak:

scope :recent_books, lambda 
  { |since_when| where("created_at >= ?", since_when) }
  # Note the `where` is making use of AREL syntax added in Rails 3.

Wreszcie możesz wyłączyć domyślny zakres za pomocą:

Book.with_exclusive_scope { find(:all) } 

lub nawet lepiej:

Book.unscoped.all

co wyłączy dowolny filtr (warunki) lub sortuje (sortuj według).

Zauważ, że pierwsza wersja działa w Rails2 +, podczas gdy druga (bez zakresu) jest tylko dla Rails3 +


Więc ... jeśli myślisz, hmm, więc są to metody jak wtedy ... tak, właśnie takie są te zakresy!
Są jak posiadanie, def self.method_name ...code... endale jak zawsze z rubinem, są ładnymi małymi skrótami syntaktycznymi (lub „cukrowymi”), aby ułatwić ci wszystko!

W rzeczywistości są to metody na poziomie klasy, ponieważ działają na 1 zestawie „wszystkich” rekordów.

Zmienia się jednak ich format. W przypadku szyn 4 ostrzeżenie o wycofaniu jest wyświetlane podczas używania #scope bez przechodzenia przez obiekt na żądanie. Na przykład zakres: czerwony, gdzie (kolor: „czerwony”) należy zmienić na scope :red, -> { where(color: 'red') }.

Na marginesie, w przypadku nieprawidłowego użycia domyślny _scope może być niewłaściwie wykorzystywany / nadużywany.
Dotyczy to głównie sytuacji, w której przyzwyczaja się do działań takich jak whereograniczanie (filtrowanie) domyślnego wyboru ( zły pomysł na domyślny), a nie tylko do porządkowania wyników.
Do whereselekcji użyj zwykłych nazwanych zakresów. i dodaj ten zakres do zapytania, np. Book.all.publishedgdzie publishedjest nazwany zakres.

Podsumowując, zakresy są naprawdę świetne i pomagają wepchnąć rzeczy do modelu dla podejścia DRYer typu „gruby model kontrolera”.

Michael Durrant
źródło
1
Proszę rozważyć ostrzeżenie Dave'a Thomasa przed użyciem default_scope przed użyciem go, jak opisano w tym poście: pragdave.blogs.pragprog.com/pragdave/2012/03/…
reto
3
czy nie byłoby to bezpieczniejsze default_scope { order("#{table_name}.created_at DESC") }?
cyrilchampier
37
Szyny 4:default_scope { order(created_at: :desc) }
Marcus
2
Przynajmniej 4.2.6wydaje się sortować według updated_atnie created_at.
Ain Tohvri,
2
@AinTohvri ma rację. Właśnie mnie to zaskoczyło w Rails 4.2. Po co sortować updated_atdomyślnie? : - |
sixty4bit
112

Szybka aktualizacja doskonałej odpowiedzi Michaela powyżej.

W przypadku Rails 4.0+ musisz umieścić swój sort w bloku takim jak ten:

class Book < ActiveRecord::Base
  default_scope { order('created_at DESC') }
end

Zauważ, że instrukcja zamówienia jest umieszczona w bloku oznaczonym nawiasami klamrowymi.

Zmienili to, ponieważ zbyt łatwo było wprowadzić coś dynamicznego (jak obecny czas). To usuwa problem, ponieważ blok jest oceniany w czasie wykonywania. Jeśli nie użyjesz bloku, otrzymasz następujący błąd:

Usunięto obsługę wywoływania #default_scope bez bloku. Na przykład zamiast default_scope where(color: 'red'), użyj default_scope { where(color: 'red') }. (Alternatywnie możesz po prostu przedefiniować self.default_scope.)

Jak wspomina @Dan w swoim komentarzu poniżej, możesz wykonać bardziej rubinową składnię, jak poniżej:

class Book < ActiveRecord::Base
  default_scope { order(created_at: :desc) }
end

lub z wieloma kolumnami:

class Book < ActiveRecord::Base
  default_scope { order({begin_date: :desc}, :name) }
end

Dzięki @Dan !

Paul Oliver
źródło
28
W szynach 4 można to również zapisać tak default_scope { order(created_at: :desc) }, jakbyś, podobnie jak ja, próbował zminimalizować składnię sql w szynach. <br/> Jeśli masz wiele kolumn do uporządkowania i chcesz użyć nowej składni, może być konieczne zawinięcie opisu kolumny w takich wąsachdefault_scope { order({begin_date: :desc}, :name) }
Dan
1
@ Dan - Twój komentarz nie tylko eliminuje SQL, ale jest to bardziej składnia w języku Rubyish.
B Seven