I am using running a simple find all and paginating with willpaginate, but I'd also like to have the query sorted by the user. The first solution that came to mind was just use a params[:sort]
http://localhost:3000/posts/?sort=created_at+DESC
@posts = Post.paginate :page => params[:page], :order => params[:sort]
But the problem with his approach is that the query is defaulting as sorting by ID and I want it to be created_at.
Is this a safe approach to sorting and is there a way to default to created_at?
I’d use a named scope for providing the default order (available since Rails 2.1).
You’d add the scope in your Post model:
named_scope :ordered, lambda {|*args| {:order => (args.first || 'created_at DESC')} }
Then you can call:
@posts = Post.ordered.paginate :page => params[:page]
The example above will use the default order from the named_scope
(created_at DESC
), but you can also provide a different one:
@posts = Post.ordered('title ASC').paginate :page => params[:page]
You could use that with Romulo’s suggestion:
sort_params = { "by_date" => "created_at", "by_name" => "name" }
@posts = Post.ordered(sort_params[params[:sort]]).paginate :page => params[:page]
If params[:sort]
isn’t found in sort_params
and returns nil
then named_scope
will fall back to using the default order.
Railscasts has some great info on named_scopes.
The Ruby idiom to set a default would be:
@posts = Post.paginate :page => params[:page], :order => params[:sort] || "created_at"
But the approach isn't safe. The paginate method will not bother with a parameter like "created_at; DROP DATABASE mydatabase;"
. Instead, you could use a dictionary of valid sort parameters (untested):
sort_params = { "by_date" => "created_at", "by_name" => "name" }
@posts = Post.paginate :page => params[:page], :order => sort_params[params[:sort] || "by_date"]
So that the URI becomes:
http://localhost:3000/posts/?sort=by_date
In general, the way to supply default values for Hash and Hash-like objects is to use fetch
:
params.fetch(:sort){ :created_at }
A lot of people just use ||
though:
params[:sort] || :created_at
I prefer fetch
myself as being more explicit, plus it doesn't break when false
is a legitimate value.
I prefer this idiom:
@posts = Post.paginate :page=>page, :order=>order
...
def page
params[:page] || 1
end
def order
params[:order] || 'created_at ASC'
end
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With