rails で /users/:id ではなく /:username な routing にする方法

rails で普通に scaffold すると id が URL に入って example.com/users/:id となる。
これを twitter.com/takady7 とか github.com/takady みたいにしたい時がある。
実現には 2 通りの方法があると思われる。

to_param を使う方法

activerecord に to_param というメソッドがあって、これを使うと URL の :id の部分に id 以外を指定できるようになる。

user.rb

class User < ActiveRecord::Base
  validates_presence_of :username
  validates_uniqueness_of :username, case_sensitive: false

  def to_param
    username
  end
end

users_controller.rb

class UsersController < ApplicationController
  def show
    @user = User.find_by(username: params[:id])
  end
end

routes.rb

Rails.application.routes.draw do
  resources :users, path: '/', only: [:show, :edit, :update, :destroy]
end
$ rake routes
    Prefix Verb   URI Pattern                        Controller#Action
 edit_user GET    /:id/edit(.:format)                users#edit
      user GET    /:id(.:format)                     users#show
           PATCH  /:id(.:format)                     users#update
           PUT    /:id(.:format)                     users#update
           DELETE /:id(.:format)                     users#destroy

routing で param を設定する方法

User クラスに to_param を定義せずに、 routes.rb で設定する方法。
params[:username] というふうに渡ってくるので、こちらの方が素直な気がして個人的にはこちらを使いたい。

users_controller.rb

class UsersController < ApplicationController
  def show
    @user = User.find_by(username: params[:username])
  end
end

routes.rb

Rails.application.routes.draw do
  resources :users, param: :username, path: '/', only: [:show, :edit, :update, :destroy]
end
$ rake routes
    Prefix Verb   URI Pattern                        Controller#Action
 edit_user GET    /:username/edit(.:format)          users#edit
      user GET    /:username(.:format)               users#show
           PATCH  /:username(.:format)               users#update
           PUT    /:username(.:format)               users#update
           DELETE /:username(.:format)               users#destroy

参考