web-dev-qa-db-fra.com

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

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

Ainsi, lorsque je fais une .where() sans spécifier de .order(), il utilise le tri par défaut. Mais si je spécifie une .order(), elle remplace la valeur par défaut.

241
Justin Tanner

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 aurez besoin à la place:

default_scope order('created_at DESC')

Pour Rails 2.x:

default_scope :order => 'created_at DESC'

created_at est le champ sur lequel vous souhaitez effectuer le tri par défaut.

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

scope

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

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

Pour Rails 2, vous avez besoin de named_scope.

La portée de :published vous donne Book.published au lieu de Book.find(:published => true).

Depuis Rails 3, vous pouvez "chaîner" ces méthodes en les concaténant avec des points entre elles. Ainsi, avec les portées ci-dessus, vous pouvez maintenant utiliser Book.published.confirmed.

Avec cette méthode, la requête n'est pas réellement exécutée tant que les résultats réels ne sont pas nécessaires (évaluation différée). Ainsi, 7 étendues peuvent être chaînées ensemble, mais ne produisent qu'une requête de base de données, afin d'é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 ID utilisateur (quelque chose qui changera au moment de l'exécution et qui nécessitera donc 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 même mieux:

Book.unscoped.all

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

Notez que la première version fonctionne dans Rails2 + alors que la seconde (non recouverte) ne concerne que Rails3 +


So ... si vous y réfléchissez, hmm, ce sont donc des méthodes, alors ... Eh oui, c'est exactement ce que sont ces scopes!
C’est comme avoir def self.method_name ...code... end mais comme toujours avec Ruby, ce sont de jolis petits raccourcis syntaxiques (ou 'sucre') pour vous faciliter la tâche!

En fait, il s’agit de méthodes de niveau classe, car elles opèrent sur le jeu 1 d’enregistrements.

Leur format change cependant, avec Rails 4, il y a un avertissement de dépréciation lorsque vous utilisez #scope sans transmettre d'objet appelable. Par exemple scope: red, where (color: 'red') devrait être changé en scope :red, -> { where(color: 'red') }.

En remarque, en cas d'utilisation incorrecte, valeur par défaut _ scope peut être mal utilisé/maltraité.
Ceci concerne principalement le moment où il est utilisé pour des actions telles que where limitant (filtrant) la sélection par défaut (a - mauvaise idée pour un défaut) plutôt que d'être simplement utilisé pour classer les résultats.
Pour les sélections where, utilisez simplement les portées nommées habituelles. et ajoutez cette étendue dans la requête, par ex. Book.all.publishedpublished est une portée nommée.

En conclusion, les oscilloscopes sont vraiment géniaux et vous aident à intégrer le modèle d’approche DRYer à un "contrôleur de modèle mince".

518
Michael Durrant

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 de commande 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') }. (Sinon, vous pouvez simplement redéfinir self.default_scope.)

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

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 !

108
Paul Oliver

Vous pouvez utiliser default_scope pour implémenter un ordre de tri par défaut http://api.rubyonrails.org/classes/ActiveRecord/Scoping/Default/ClassMethods.html

6
Slobodan Kovacevic