开发者

Pretty Paths in Rails

I have a category model and I'm routing it using the default scaffolding of resources :categories. I'm wondering if there's a way to change the paths from /category/:id to /category/:name. I added:

match "/categories/:name" => "categories#show"

above the resources line in routes.rb and changed the show action in the controller to do:

@category = Category.find_by_name(params[:name])

it works, but the 'magic paths' such as link_to some_category still use the :id format.

Is there a way to do this? If this is a bad idea (due to 开发者_如何学Gosome possible way in which rails works internally), is there another way to accomplish this? So that /categories/music, for example, and /categories/3 both work?


Rails has a nifty model instance method called to_param, and it's what the paths use. It defaults to id, but you can override it and produce something like:

class Category < ActiveRecord::Base
  def to_param
    name
  end
end

cat = Category.find_by_name('music')
category_path(cat)  # => "/categories/music"

For more info, check the Rails documentation for to_param.

EDIT:

When it comes to category names which aren't ideal for URLs, you have multiple options. One is, as you say, to gsub whitespaces with hyphens and vice versa when finding the record. However, a safer option would be to create another column on the categories table called name_param (or similar). Then, you can use it instead of the name for, well, all path and URL related business. Use the parameterize inflector to create a URL-safe string. Here's how I'd do it:

class Category < ActiveRecord::Base
  after_save :create_name_param

  def to_param
    name_param
  end

  private
    def create_name_param
      self.name_param = name.parameterize
    end
end

# Hypothetical
cat = Category.create(:name => 'My. Kewl. Category!!!')
category_path(cat)  # => "/categories/my-kewl-category"

# Controller
@category = Category.find_by_name_param(param[:id]) # <Category id: 123, name: 'My. Kewl. Category!!!'>


If you don't want to to break existing code that relying on model id you could define your to_param like this:

 def to_param
   "#{id}-#{name}"
 end

so your url will be: http://path/1-some-model and you still can load your model with Model.find(params[:id]) because:

"123-hello-world".to_i => 123


Although possibly more than you need, you may also want to look into 'human readable urls' support like friendly_id or one of the others (for instance, if you need unicode support, etc.) that are described here at Ruby Toolbox.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜