Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to rename the default identifier param "id" in Rails' map.resources()?

I like all the default routes that are generated by Rail's map.resources. But, there are cases where I would like to use a non-numeric identifier in my routes. For example, If have a nested route consist of users and their articles, a standard route could be written as such:

map.resources :users, :has_many => [:articles] # => e.g. '/users/:id/articles/:id'

However, there are many advantages / reasons not to use the default numerical identifier generated by Rails. Is there a way to replace the default :id params to another canonical identifier of my choice without resulting to writing custom routes for every standard action? Say if I want a route in the following format:

'/users/:login/articles/:id'

Is this kind of routes achievable using map.resources?

like image 832
newtonapple Avatar asked May 01 '09 06:05

newtonapple


2 Answers

As of Rails 2.3, it's not possible to change the parameter name and still use the automatic routing that #resources provides.

As a workaround, you can map articles with a :path_prefix and :name_prefix:

map.resources :articles, :path_prefix => "/users/:login",
                         :name_prefix => "user_"

The :path_prefix affects the URL, and the :name_prefix affects the generated named routes, so you'll end up with these routes:

    user_articles GET    /users/:login/articles(.:format)          {:controller=>"articles", :action=>"index"}
                  POST   /users/:login/articles(.:format)          {:controller=>"articles", :action=>"create"}
 new_user_article GET    /users/:login/articles/new(.:format)      {:controller=>"articles", :action=>"new"}
edit_user_article GET    /users/:login/articles/:id/edit(.:format) {:controller=>"articles", :action=>"edit"}
     user_article GET    /users/:login/articles/:id(.:format)      {:controller=>"articles", :action=>"show"}
                  PUT    /users/:login/articles/:id(.:format)      {:controller=>"articles", :action=>"update"}
                  DELETE /users/:login/articles/:id(.:format)      {:controller=>"articles", :action=>"destroy"}

As a general rule-of-thumb, though, I'd stick with the Rails default convention of :user_id, with the routing you posted in your question. It's generally understood that :id and :user_id don't necessarily imply "numeric identifier" — they imply "resource identifier," whatever that might be. And by sticking to the default convention, your code will be easier to understand for anyone who's used resource routes in Rails.

To use a non-numeric identifier for a resource, just redefine #to_param in your model. Then, make sure to use a finder in your controller that will find by this identifier (rather than the numeric ID), such as User#find_by_login!.

like image 70
chrisk Avatar answered Nov 14 '22 09:11

chrisk


You can change the default of using the ID in URLs by overriding to_param in your model. e.g.

class User < ActiveRecord::Base
  def to_param
    login
  end
end

user_articles_path(@user) => "/users/:login/articles"

The only other change you'll need to make is to find users by login rather than by ID in your controllers.

like image 35
Paul Horsfall Avatar answered Nov 14 '22 10:11

Paul Horsfall