Changing the id parameter in Rails routing

28,887

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.

Share:
28,887

Related videos on Youtube

joeellis
Author by

joeellis

Updated on July 30, 2020

Comments

  • joeellis
    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
      Andrew Philips over 10 years
      I 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
      blnc about 7 years
      Make sure to read the answers after the accepted one
  • joeellis
    joeellis about 14 years
    That'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
    joeellis about 14 years
    I guess this is the only way to go about it for now. Seems kind of roundabout though.
  • iNulty
    iNulty about 11 years
    That appears to be a CanCan method. Its not mentioned in the rails api at all. apidock.com api.rubyonrails.org
  • Kashif Umair Liaqat
    Kashif Umair Liaqat about 11 years
    Yes, that is a CanCan method. I forgot to mention that in my answer. I have updated the answer.
  • Andrew Philips
    Andrew Philips over 10 years
    Thanks 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
    omnikron over 10 years
    this 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
    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 do user_path @user.username) ...but yes you're right, this is elegant when you just want routing functionality.
  • Ben
    Ben over 9 years
    the 2) is most of the time missed when when this question appears; this should be the accepted answer