Ordem de classificação padrão para um modelo de trilhos?

255

Gostaria de especificar uma ordem de classificação padrão no meu modelo.

Assim, quando eu faço um .where()sem especificar um, .order()ele usa a classificação padrão. Mas se eu especificar um .order(), ele substituirá o padrão.

Justin Tanner
fonte

Respostas:

544

default_scope

Isso funciona para o Rails 4+:

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

Para o Rails 2.3, 3, você precisa disso:

default_scope order('created_at DESC')

Para o Rails 2.x:

default_scope :order => 'created_at DESC'

Onde created_até o campo no qual você deseja que a classificação padrão seja feita.

Nota: ASC é o código a ser usado para Crescente e DESC é para decrescente ( desc, NÃO dsc !).

scope

Depois de se acostumar, você também pode usar scope:

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

Para o Rails 2, você precisa named_scope.

:publishedescopo fornece a você em Book.publishedvez de Book.find(:published => true).

Desde o Rails 3, você pode 'encadear' esses métodos, concatenando-os com períodos entre eles; portanto, com os escopos acima, agora você pode usar Book.published.confirmed.

Com esse método, a consulta não é realmente executada até que os resultados reais sejam necessários (avaliação lenta); portanto, 7 escopos podem ser encadeados, mas resultando apenas em 1 consulta real no banco de dados, para evitar problemas de desempenho ao executar 7 consultas separadas.

Você pode usar um parâmetro passado como uma data ou um user_id (algo que será alterado no tempo de execução e, portanto, precisará dessa 'avaliação lenta', com um lambda, assim:

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, você pode desativar o escopo padrão com:

Book.with_exclusive_scope { find(:all) } 

ou melhor ainda:

Book.unscoped.all

que desativará qualquer filtro (condições) ou classificação (ordenar por).

Observe que a primeira versão funciona no Rails2 +, enquanto a segunda (sem escopo) é apenas para o Rails3 +


Então ... se você está pensando, hmm, então esses são apenas métodos, então ... sim, é exatamente isso que esses escopos são!
Eles são como ter, def self.method_name ...code... endmas como sempre com ruby, são pequenos atalhos sintáticos (ou "açúcar") para facilitar as coisas para você!

Na verdade, eles são métodos de nível de classe, pois operam no conjunto 1 de 'todos' registros.

Seu formato está mudando, no entanto, com os trilhos 4, há aviso de reprovação ao usar #scope sem passar um objeto que pode ser chamado. Por exemplo, scope: red, onde (color: 'red') deve ser alterado para scope :red, -> { where(color: 'red') }.

Como observação, quando usado incorretamente, o _scope padrão pode ser mal utilizado / abusado.
Isso ocorre principalmente quando é usado para ações como wherelimitar (filtrar) a seleção padrão (uma má idéia para um padrão), em vez de apenas ser usado para ordenar resultados.
Para whereseleções, basta usar os escopos nomeados regulares. e adicione esse escopo na consulta, por exemplo, Book.all.publishedonde publishedestá um escopo nomeado.

Em conclusão, os escopos são realmente ótimos e ajudam você a inserir as coisas no modelo para uma abordagem DRYer de 'modelo fino de controlador de gordura'.

Michael Durrant
fonte
1
Por favor, considere o aviso de Dave Thomas sobre o uso do default_scope antes de usá-lo, conforme descrito nesta publicação: pragdave.blogs.pragprog.com/pragdave/2012/03/…
reto
3
não seria mais seguro fazer isso default_scope { order("#{table_name}.created_at DESC") }?
Cyrilchampier 18/03/2014
37
Rails 4:default_scope { order(created_at: :desc) }
Marcus
2
Pelo menos 4.2.6parece classificar por updated_atnão created_at.
Ain Tohvri 14/09/16
2
@AinTohvri está certo. Isso me pegou de surpresa no Rails 4.2. Por que classificar updated_atpor padrão? : - |
sixty4bit
112

Uma atualização rápida da excelente resposta de Michael acima.

Para o Rails 4.0+, você precisa colocar sua classificação em um bloco como este:

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

Observe que a declaração de ordem é colocada em um bloco indicado pelas chaves.

Eles mudaram porque era muito fácil transmitir algo dinâmico (como a hora atual). Isso remove o problema porque o bloco é avaliado em tempo de execução. Se você não usar um bloco, receberá este erro:

O suporte para chamar #default_scope sem um bloco foi removido. Por exemplo, em vez de default_scope where(color: 'red'), por favor use default_scope { where(color: 'red') }. (Como alternativa, você pode apenas redefinir self.default_scope.)

Como @Dan menciona em seu comentário abaixo, você pode fazer uma sintaxe mais rubyish como esta:

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

ou com várias colunas:

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

Obrigado @Dan !

Paul Oliver
fonte
28
No rails 4, isso também pode ser escrito como default_scope { order(created_at: :desc) }se você tentasse minimizar a sintaxe sql nos rails. <br/> Se você tem várias colunas para ordenar e deseja usar a nova sintaxe, pode ser necessário agrupar a descrição desc. colunas em bigodes como estedefault_scope { order({begin_date: :desc}, :name) }
Dan
1
@ Dan - Não apenas o seu comentário elimina o SQL, mas também uma sintaxe Rubyish.
B Seven