Ordre de tri par défaut pour un modèle de rails?

255

Je voudrais spécifier un ordre de tri par défaut dans mon modèle.

De sorte que lorsque je fais un .where()sans spécifier un, .order()il utilise le tri par défaut. Mais si je spécifie un .order(), il remplace la valeur par défaut.

Justin Tanner
la source

Réponses:

544

default_scope

Cela fonctionne pour Rails 4+:

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

Pour Rails 2.3, 3, vous en avez besoin à la place:

default_scope order('created_at DESC')

Pour Rails 2.x:

default_scope :order => 'created_at DESC'

Où se created_attrouve le champ sur lequel vous souhaitez effectuer le tri par défaut.

Remarque: ASC est le code à utiliser pour Croissant et DESC est pour décroissant ( desc, PAS dsc !).

scope

Une fois que vous y êtes habitué, vous pouvez également utiliser scope:

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

Pour Rails 2 dont vous avez besoin named_scope.

:publishedportée vous donne Book.publishedau lieu de Book.find(:published => true).

Depuis Rails 3, vous pouvez `` enchaîner '' ces méthodes en les concaténant avec des points entre elles, donc avec les étendues ci-dessus, vous pouvez maintenant utiliser Book.published.confirmed.

Avec cette méthode, la requête n'est pas réellement exécutée jusqu'à ce que les résultats réels soient nécessaires (évaluation paresseuse), donc 7 étendues peuvent être chaînées ensemble mais ne résultant qu'en 1 requête de base de données réelle, pour éviter des problèmes de performances lors de l'exécution de 7 requêtes distinctes.

Vous pouvez utiliser un paramètre passé tel qu'une date ou un user_id (quelque chose qui changera au moment de l'exécution et aura donc besoin de cette 'évaluation paresseuse', avec un lambda, comme ceci:

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

Enfin, vous pouvez désactiver la portée par défaut avec:

Book.with_exclusive_scope { find(:all) } 

ou encore mieux:

Book.unscoped.all

ce qui désactivera tout filtre (conditions) ou tri (ordre par).

Notez que la première version fonctionne dans Rails2 + tandis que la seconde (non étendue) est uniquement pour Rails3 +


Donc ... si vous pensez, hmm, donc ce sont juste des méthodes alors ..., ouais, c'est exactement ce que sont ces étendues!
Ils sont comme avoir def self.method_name ...code... endmais comme toujours avec rubis ce sont de jolis petits raccourcis syntaxiques (ou «sucre») pour vous faciliter les choses!

En fait, ce sont des méthodes au niveau de la classe car elles opèrent sur le 1 jeu d'enregistrements «tous».

Leur format change cependant, avec les rails 4, il y a un avertissement de dépréciation lors de l'utilisation de #scope sans passer un objet appelable. Par exemple, portée: rouge, où (couleur: «rouge») doit être remplacé parscope :red, -> { where(color: 'red') } .

En remarque, lorsqu'il est utilisé de manière incorrecte, _scope par défaut peut être mal utilisé / abusé.
Cela concerne principalement le moment où il est utilisé pour des actions telles que wherela limitation (filtrage) de la sélection par défaut (une mauvaise idée pour une valeur par défaut) plutôt que d'être simplement utilisé pour ordonner les résultats.
Pour les wheresélections, utilisez simplement les étendues nommées standard. et ajoutez cette étendue dans la requête, par exemple Book.all.publishedpublishedest une étendue nommée.

En conclusion, les portées sont vraiment géniales et vous aident à pousser les choses dans le modèle pour une approche DRYer de `` contrôleur de modèle mince ''.

Michael Durrant
la source
1
Veuillez considérer l'avertissement de Dave Thomas sur l'utilisation de default_scope avant de l'utiliser comme décrit dans ce post: pragdave.blogs.pragprog.com/pragdave/2012/03/…
reto
3
ne serait-il pas plus sûr de le faire default_scope { order("#{table_name}.created_at DESC") }?
cyrilchampier
37
Rails 4: default_scope { order(created_at: :desc) }
Marcus
2
4.2.6Semble au moins trier par updated_atnon created_at.
Ain Tohvri
2
@AinTohvri a raison. Cela m'a pris par surprise dans Rails 4.2. Pourquoi trier updated_atpar défaut? : - |
sixty4bit
112

Une mise à jour rapide de l'excellente réponse de Michael ci-dessus.

Pour Rails 4.0+, vous devez placer votre tri dans un bloc comme celui-ci:

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

Notez que l'instruction order est placée dans un bloc désigné par les accolades.

Ils l'ont changé parce qu'il était trop facile de passer quelque chose de dynamique (comme l'heure actuelle). Cela supprime le problème car le bloc est évalué au moment de l'exécution. Si vous n'utilisez pas de bloc, vous obtiendrez cette erreur:

La prise en charge de l'appel de #default_scope sans bloc est supprimée. Par exemple, au lieu de default_scope where(color: 'red'), veuillez utiliser default_scope { where(color: 'red') }. (Alternativement, vous pouvez simplement redéfinir self.default_scope.)

Comme @Dan mentionne dans son commentaire ci-dessous, vous pouvez faire une syntaxe plus rubis comme celle-ci:

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

ou avec plusieurs colonnes:

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

Merci @Dan !

Paul Oliver
la source
28
Dans les rails 4, cela peut également être écrit comme default_scope { order(created_at: :desc) }si, comme moi, vous essayez de minimiser la syntaxe SQL dans les rails. <br/> Si vous avez plusieurs colonnes à classer et que vous souhaitez utiliser la nouvelle syntaxe, vous devrez peut-être envelopper la desc colonnes dans des moustaches comme celle-cidefault_scope { order({begin_date: :desc}, :name) }
Dan
1
@Dan - Non seulement votre commentaire élimine SQL, mais c'est une syntaxe plus Rubyish.
B Seven