I would like to specify a default sort order in my model.
So that when I do a .where()
without specifying an .order()
it uses the default sort. But if I specify an .order()
, it overrides the default.
ruby-on-rails
I would like to specify a default sort order in my model.
So that when I do a .where()
without specifying an .order()
it uses the default sort. But if I specify an .order()
, it overrides the default.
Best Answer
default_scope
This works for Rails 4+:
For Rails 2.3, 3, you need this instead:
For Rails 2.x:
Where
created_at
is the field you want the default sorting to be done on.Note: ASC is the code to use for Ascending and DESC is for descending (
desc
, NOTdsc
!).scope
Once you're used to that you can also use
scope
:For Rails 2 you need
named_scope
.:published
scope gives youBook.published
instead ofBook.find(:published => true)
.Since Rails 3 you can 'chain' those methods together by concatenating them with periods between them, so with the above scopes you can now use
Book.published.confirmed
.With this method, the query is not actually executed until actual results are needed (lazy evaluation), so 7 scopes could be chained together but only resulting in 1 actual database query, to avoid performance problems from executing 7 separate queries.
You can use a passed in parameter such as a date or a user_id (something that will change at run-time and so will need that 'lazy evaluation', with a lambda, like this:
Finally you can disable default scope with:
or even better:
which will disable any filter (conditions) or sort (order by).
Note that the first version works in Rails2+ whereas the second (unscoped) is only for Rails3+
So ... if you're thinking, hmm, so these are just like methods then..., yup, that's exactly what these scopes are!
They are like having
def self.method_name ...code... end
but as always with ruby they are nice little syntactical shortcuts (or 'sugar') to make things easier for you!In fact they are Class level methods as they operate on the 1 set of 'all' records.
Their format is changing however, with rails 4 there are deprecation warning when using #scope without passing a callable object. For example scope :red, where(color: 'red') should be changed to
scope :red, -> { where(color: 'red') }
.As a side note, when used incorrectly, default_scope can be misused/abused.
This is mainly about when it gets used for actions like
where
's limiting (filtering) the default selection (a bad idea for a default) rather than just being used for ordering results.For
where
selections, just use the regular named scopes. and add that scope on in the query, e.g.Book.all.published
wherepublished
is a named scope.In conclusion, scopes are really great and help you to push things up into the model for a 'fat model thin controller' DRYer approach.