Orden de clasificación predeterminado para un modelo de rieles?

255

Me gustaría especificar un orden de clasificación predeterminado en mi modelo.

De modo que cuando hago un .where()sin especificar un .order(), usa el tipo predeterminado. Pero si especifico un .order(), anula el valor predeterminado.

Justin Tanner
fuente

Respuestas:

544

default_scope

Esto funciona para Rails 4+:

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

Para Rails 2.3, 3, necesita esto en su lugar:

default_scope order('created_at DESC')

Para Rails 2.x:

default_scope :order => 'created_at DESC'

¿Dónde created_atestá el campo en el que desea que se realice la ordenación predeterminada?

Nota: ASC es el código que se utilizará para ascendente y DESC es para descendente ( desc, NO dsc !).

scope

Una vez que esté acostumbrado a eso, también puede usar scope:

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

Para Rails 2 necesitas named_scope.

:publishedalcance te da en Book.publishedlugar de Book.find(:published => true).

Desde Rails 3 puede 'encadenar' esos métodos juntos concatenando con puntos entre ellos, por lo que con los ámbitos anteriores ahora puede usar Book.published.confirmed.

Con este método, la consulta no se ejecuta realmente hasta que se necesitan resultados reales (evaluación diferida), por lo que se podrían encadenar 7 ámbitos pero solo dar como resultado 1 consulta de base de datos real, para evitar problemas de rendimiento al ejecutar 7 consultas separadas.

Puede usar un parámetro pasado como una fecha o un user_id (algo que cambiará en tiempo de ejecución y, por lo tanto, necesitará esa 'evaluación diferida', con una lambda, como esta:

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 puede deshabilitar el alcance predeterminado con:

Book.with_exclusive_scope { find(:all) } 

o mejor:

Book.unscoped.all

que deshabilitará cualquier filtro (condiciones) u ordenar (ordenar por).

Tenga en cuenta que la primera versión funciona en Rails2 +, mientras que la segunda (sin ámbito) es solo para Rails3 +


Entonces ... si estás pensando, hmm, entonces estos son como métodos, entonces ... ¡sí, eso es exactamente lo que son estos ámbitos!
Son como tener, def self.method_name ...code... endpero como siempre con el rubí, son pequeños atajos sintácticos (o 'azúcar') para hacerte las cosas más fáciles.

De hecho, son métodos de nivel de clase, ya que operan en el 1 conjunto de registros 'todos'.

Sin embargo, su formato está cambiando, con los rieles 4 hay una advertencia de desaprobación cuando se usa #scope sin pasar un objeto invocable. Por ejemplo, alcance: rojo, donde (color: 'rojo') debe cambiarse a scope :red, -> { where(color: 'red') }.

Como nota al margen, cuando se usa incorrectamente, el _scope predeterminado puede ser mal utilizado / abusado.
Esto se trata principalmente cuando se utiliza para acciones como wherelimitar (filtrar) la selección predeterminada (una mala idea para un valor predeterminado) en lugar de simplemente usarse para ordenar resultados.
Para las whereselecciones, solo use los ámbitos con nombre normal. y agregue ese alcance en la consulta, por ejemplo, Book.all.publisheddónde publishedes un alcance con nombre.

En conclusión, los alcances son realmente geniales y lo ayudan a impulsar las cosas dentro del modelo para un enfoque SECO de 'controlador delgado de modelo grueso'.

Michael Durrant
fuente
1
Tenga en cuenta la advertencia de Dave Thomas sobre el uso de default_scope antes de usarlo como se describe en esta publicación: pragdave.blogs.pragprog.com/pragdave/2012/03/…
reto
3
¿No sería más seguro hacerlo default_scope { order("#{table_name}.created_at DESC") }?
cyrilchampier
37
Rieles 4:default_scope { order(created_at: :desc) }
Marcus
2
Al menos 4.2.6parece ordenar por updated_atno created_at.
Ain Tohvri
2
@AinTohvri tiene razón. Esto me tomó por sorpresa en Rails 4.2. ¿Por qué ordenar updated_atpor defecto? : - |
sixty4bit
112

Una actualización rápida a la excelente respuesta de Michael anterior.

Para Rails 4.0+ necesitas poner tu clasificación en un bloque como este:

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

Observe que la declaración de orden se coloca en un bloque denotado por las llaves.

Lo cambiaron porque era demasiado fácil pasar algo dinámico (como la hora actual). Esto elimina el problema porque el bloque se evalúa en tiempo de ejecución. Si no usa un bloque, obtendrá este error:

Se elimina la compatibilidad para llamar a #default_scope sin un bloque. Por ejemplo, en lugar de default_scope where(color: 'red'), por favor use default_scope { where(color: 'red') }. (Alternativamente, puede redefinir self.default_scope).

Como @Dan menciona en su comentario a continuación, puede hacer una sintaxis más rubí como esta:

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

o con múltiples columnas:

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

Gracias @ Dan !

Paul Oliver
fuente
28
En los rieles 4, esto también se puede escribir como default_scope { order(created_at: :desc) }si, como yo, intentara minimizar la sintaxis sql en los rieles. <br/> Si tiene varias columnas para ordenar y desea usar la nueva sintaxis, es posible que necesite ajustar la desc. columnas con bigotes como estedefault_scope { order({begin_date: :desc}, :name) }
Dan
1
@Dan: su comentario no solo elimina SQL, es una sintaxis más Rubyish.
B Seven