Ordinamento predefinito per un modello di binari?


255

Vorrei specificare un ordinamento predefinito nel mio modello.

In modo che quando faccio un .where()senza specificare un .order()utilizza l'ordinamento predefinito. Ma se specifico un .order(), sovrascrive quello predefinito.

Risposte:


544

default_scope

Funziona con Rails 4+:

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

Per Rails 2.3, 3, invece, ti serve questo:

default_scope order('created_at DESC')

Per Rails 2.x:

default_scope :order => 'created_at DESC'

Dov'è created_atil campo in cui si desidera eseguire l'ordinamento predefinito.

Nota: ASC è il codice da utilizzare per Ascendente e DESC per Discendente ( desc, NOT dsc !).

scope

Una volta abituato, puoi anche usare scope:

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

Per Rails 2 è necessario named_scope.

:publishedscope ti dà Book.publishedinvece di Book.find(:published => true).

Da Rails 3 puoi "concatenare" questi metodi concatenandoli con punti tra loro, quindi con gli ambiti sopra puoi ora usare Book.published.confirmed.

Con questo metodo, la query non viene effettivamente eseguita fino a quando non sono necessari risultati effettivi (valutazione lazy), quindi è possibile concatenare 7 ambiti insieme ma solo 1 query del database effettiva, per evitare problemi di prestazioni dall'esecuzione di 7 query separate.

È possibile utilizzare un parametro passato come una data o un user_id (qualcosa che cambierà in fase di esecuzione e quindi avrà bisogno di una "valutazione pigra", con un lambda, come questo:

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

Finalmente puoi disabilitare l'ambito predefinito con:

Book.with_exclusive_scope { find(:all) } 

o anche meglio:

Book.unscoped.all

che disabiliterà qualsiasi filtro (condizioni) o ordinamento (ordina per).

Nota che la prima versione funziona in Rails2 + mentre la seconda (senza frame) è solo per Rails3 +


Quindi ... se stai pensando, hmm, quindi questi sono proprio come metodi allora ... sì, è esattamente quello che sono questi scopi!
Sono come avere, def self.method_name ...code... endma come sempre con il rubino sono delle belle scorciatoie sintattiche (o "zucchero") per rendere le cose più facili per te!

In realtà sono metodi a livello di classe in quanto operano su 1 set di record "tutti".

Il loro formato sta tuttavia cambiando, con le rotaie 4 ci sono avvisi di deprecazione quando si utilizza #scope senza passare un oggetto richiamabile. Ad esempio scope: red, dove (color: 'red') dovrebbe essere cambiato in scope :red, -> { where(color: 'red') }.

Come nota a margine , se usato in modo errato, _scope predefinito può essere utilizzato in modo improprio / abusato.
Si tratta principalmente di quando viene utilizzato per azioni come wherelimitare (filtrare) la selezione predefinita (una cattiva idea per un valore predefinito) piuttosto che essere utilizzato solo per ordinare i risultati.
Per le whereselezioni, basta usare i normali ambiti con nome. e aggiungere tale ambito nella query, ad es. Book.all.publisheddove si publishedtrova un ambito denominato.

In conclusione, gli ambiti sono davvero fantastici e ti aiutano a spingere le cose verso l'alto nel modello per un approccio DRYer "modello grasso modello sottile".


1
Si prega di considerare l'avvertimento di Dave Thomas sull'uso di default_scope prima di usarlo come descritto in questo post: pragdave.blogs.pragprog.com/pragdave/2012/03/…
reto del

3
non sarebbe più sicuro farlo default_scope { order("#{table_name}.created_at DESC") }?
cyrilchampier,

37
Rotaie 4:default_scope { order(created_at: :desc) }
Marcus,

2
Almeno 4.2.6sembra updated_atnon ordinare created_at.
Ain Tohvri,

2
@AinTohvri ha ragione. Questo mi ha sorpreso in Rails 4.2. Perché ordinare updated_atper impostazione predefinita? : - |
sixty4bit,

112

Un rapido aggiornamento all'eccellente risposta di Michael sopra.

Per Rails 4.0+ devi mettere il tuo ordinamento in un blocco come questo:

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

Si noti che l'istruzione dell'ordine viene posizionata in un blocco indicato dalle parentesi graffe.

L'hanno cambiato perché era troppo facile passare in qualcosa di dinamico (come l'ora corrente). Questo rimuove il problema perché il blocco viene valutato in fase di esecuzione. Se non usi un blocco otterrai questo errore:

Il supporto per la chiamata di #default_scope senza blocco viene rimosso. Ad esempio invece di default_scope where(color: 'red'), si prega di utilizzare default_scope { where(color: 'red') }. (In alternativa puoi semplicemente ridefinire self.default_scope.)

Come menziona @Dan nel suo commento qui sotto, puoi fare una sintassi più ruby ​​come questa:

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

o con più colonne:

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

Grazie @ Dan !


28
In rails 4 questo può anche essere scritto come default_scope { order(created_at: :desc) }se, come me, si tenta di ridurre al minimo la sintassi sql in rails. <br/> Se si hanno più colonne da ordinare e si desidera utilizzare la nuova sintassi, potrebbe essere necessario racchiudere la descrizione colonne in baffi come questodefault_scope { order({begin_date: :desc}, :name) }
Dan

1
@ Dan - Non solo il tuo commento elimina SQL, ma è anche una sintassi più rubyish.
B Seven,

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.