Changing the id parameter in Rails routing
Solution 1
If I understand you correctly, what you want is to have the username
instead of id
in your url, right?
You can do that by overriding the to_param
method in your model. You can get more information here.
Solution 2
In your route you can use
resources :user, param: :username
Solution 3
For Ruby on Rails 4.1.4 (and possibly earlier versions) you need to do what both j.. and Ujjwal suggested:
1) In config/routes.rb
, add:
resources :user, param: :username
2) In app/models/user.rb
, add:
def to_param
username
end
In you only do #1 then all your routes will be correct, as can be seen from rake routes
:
$ rake routes
Prefix Verb URI Pattern Controller#Action
user_index GET /user(.:format) user#index
POST /user(.:format) user#create
new_user GET /user/new(.:format) user#new
edit_user GET /user/:username/edit(.:format) user#edit
user GET /user/:username(.:format) user#show
PATCH /user/:username(.:format) user#update
PUT /user/:username(.:format) user#update
DELETE /user/:username(.:format) user#destroy
However the helper methods that construct a url based on a User
instance will still include the id
in the url, e.g. /user/1
. To get the username
in the constructed urls, you need to override to_param
as in #2.
Solution 4
The FriendlyId gem can help you out with this. Ryan Bates has a good video tutorial on using this gem.
Solution 5
Although the answer has been accepted by the asker, but there is a simple approach to do this. Write this code in your controller.
authorize_resource :find_by => :username
and in your view, where you want to show the link, write this code.
<%= link_to "Username", user_path(u.username) %>
You don't need any other changes in your routes or controller.
UPDATE: This will only work if you are using CanCan gem.
Related videos on Youtube
joeellis
Updated on July 30, 2020Comments
-
joeellis almost 4 years
Using Ruby on Rails 3's new routing system, is it possible to change the default :id parameter
resources :users, :key => :username
come out with the following routes
/users/new /users/:username /users/:username/edit ...etc
I'm asking because although the above example is simple, it would be really helpful to do in a current project I'm working on.
Is it possible to change this parameter, and if not, is there a particular reason as to why not?
-
Andrew Philips over 10 yearsI like this question from the perspective of "How do I change the default ID used to fetch a user/object/etc.?" However, I just want to put a plug in for obscuring that default ID as it tends to be a Primary Key directly from the database and there are all sorts of security reasons why you should not expose that value (easing SQL injection attacks, guessable IDs for other users, …). In particular, using the username enables direct attack on an account (pwd guessing). Using a large random and unique value makes all of these a lot harder. Cheers.
-
blnc about 7 yearsMake sure to read the answers after the accepted one
-
-
joeellis about 14 yearsThat's kind of what I've been doing, but I just think it'd be more straightforward to do something like User.find_by_username(params[:username]) in the code. I really am just wondering why Rails doesn't allow this in general. Is it against some REST principle perhaps?
-
joeellis about 14 yearsI guess this is the only way to go about it for now. Seems kind of roundabout though.
-
iNulty about 11 yearsThat appears to be a CanCan method. Its not mentioned in the rails api at all. apidock.com api.rubyonrails.org
-
Kashif Umair Liaqat about 11 yearsYes, that is a CanCan method. I forgot to mention that in my answer. I have updated the answer.
-
Andrew Philips over 10 yearsThanks for this pointer, completely solved my problem. I came here looking for how to change this b/c, by policy, we consider fetching by Primary Key ID from web as a security issue. Instead, we add a large random and unique value to the object (SecureRandom.urlsafe_base64(15)) and use that as the ID returned from to_param and when the RESTful APIs find_by_OID. Perhaps, when I understand ruby/rails better, this can be made into a gem.
-
omnikron over 10 yearsthis should be the accepted answer, I'm much more comfortable adding an argument to a route than I am overwriting the
to_param
method -
equivalent8 about 10 years@omnikron it depends on what you trying to achive in your app, sometimes you need to do both (e.g. when you want directly pass your resource to routing helpers
user_path @user
so that you don't have to douser_path @user.username
) ...but yes you're right, this is elegant when you just want routing functionality. -
Ben over 9 yearsthe 2) is most of the time missed when when this question appears; this should be the accepted answer