我希望我的网站网址看起来像这样:
example.com/2010/02/my-first-post
我的Post
模型带有slug
字段('my-first-post')和published_on
字段(我们将从中扣除网址中的年份和月份)
我希望我的Post
模型是RESTful的,所以像url_for(@post)
这样的东西就像他们应该的那样工作,即:它应该生成上面提到的url。
有办法做到这一点吗?我知道您需要覆盖to_param
并设置map.resources :posts
并设置:requirements
选项,但我无法全部工作。
我差不多完成了,我90%在那里。使用resource_hacks plugin我可以做到这一点:
map.resources :posts, :member_path => '/:year/:month/:slug',
:member_path_requirements => {:year => /[\d]{4}/, :month => /[\d]{2}/, :slug => /[a-z0-9\-]+/}
rake routes
(...)
post GET /:year/:month/:slug(.:format) {:controller=>"posts", :action=>"show"}
并在视图中:
<%= link_to 'post', post_path(:slug => @post.slug, :year => '2010', :month => '02') %>
生成正确的example.com/2010/02/my-first-post
链接。
我希望这也能奏效:
<%= link_to 'post', post_path(@post) %>
但它需要覆盖模型中的to_param
方法。应该相当容易,除了事实,to_param
必须返回String
,而不是我想要的Hash
。
class Post < ActiveRecord::Base
def to_param
{:slug => 'my-first-post', :year => '2010', :month => '02'}
end
end
导致can't convert Hash into String
错误。
这似乎被忽略了:
def to_param
'2010/02/my-first-post'
end
因为它导致错误:post_url failed to generate from {:action=>"show", :year=>#<Post id: 1, title: (...)
(它错误地将@post对象分配给:year键)。我对如何破解它毫无头绪。
答案 0 :(得分:4)
Rails 3.x和Rails 2.x的漂亮网址,不需要任何外部插件,但遗憾的是有点破解。
的routes.rb
map.resources :posts, :except => [:show]
map.post '/:year/:month/:slug', :controller => :posts, :action => :show, :year => /\d{4}/, :month => /\d{2}/, :slug => /[a-z0-9\-]+/
application_controller.rb
def default_url_options(options = {})
# resource hack so that url_for(@post) works like it should
if options[:controller] == 'posts' && options[:action] == 'show'
options[:year] = @post.year
options[:month] = @post.month
end
options
end
post.rb
def to_param # optional
slug
end
def year
published_on.year
end
def month
published_on.strftime('%m')
end
视图
<%= link_to 'post', @post %>
注意,对于Rails 3.x,您可能希望使用此路由定义:
resources :posts
match '/:year/:month/:slug', :to => "posts#show", :as => :post, :year => /\d{4}/, :month => /\d{2}/, :slug => /[a-z0-9\-]+/
是否有任何徽章可以回答您自己的问题? ;)
顺便说一句:routing_test文件是查看使用Rails路由可以做些什么的好地方。
更新:使用default_url_options
死胡同。只有在控制器中定义了@post
变量时,发布的解决方案才有效。例如,如果@posts
变量包含帖子数组,我们就不走运了(因为default_url_options
无权访问p
@posts.each do |p|
中的{{1}} }。
所以这仍然是一个悬而未决的问题。有人帮忙吗?
答案 1 :(得分:4)
它仍然是一个黑客,但以下工作:
在application_controller.rb
:
def url_for(options = {})
if options[:year].class.to_s == 'Post'
post = options[:year]
options[:year] = post.year
options[:month] = post.month
options[:slug] = post.slug
end
super(options)
end
以下内容适用(在Rails 2.3.x和3.0.0中):
url_for(@post)
post_path(@post)
link_to @post.title, @post
etc.
这是nice soul对我的类似问题url_for of a custom RESTful resource (composite key; not just id)的回答。
答案 2 :(得分:1)
Ryan Bates在他的屏幕演员中谈到了它“如何添加自定义路线,使一些参数可选,并添加其他参数的要求。” http://railscasts.com/episodes/70-custom-routes
答案 3 :(得分:1)
这可能会有所帮助。您可以在ApplicationController
中定义一个default_url_options
方法,该方法接收传递给网址帮助程序的哈希选项,并返回您希望用于这些网址的其他选项的哈希值。
如果将帖子作为参数提供给post_path
,则会将其分配给路径的第一个(未分配的)参数。没有测试过,但它可能有用:
def default_url_options(options = {})
if options[:controller] == "posts" && options[:year].is_a?Post
post = options[:year]
{
:year => post.created_at.year,
:month => post.created_at.month,
:slug => post.slug
}
else
{}
end
end
我处于类似的情况,其中一个帖子有一个语言参数和slug参数。写post_path(@post)
将此哈希发送到default_url_options
方法:
{:language=>#<Post id: 1, ...>, :controller=>"posts", :action=>"show"}
更新:存在一个问题,即您无法覆盖该方法的url参数。传递给url帮助程序的参数优先。所以你可以这样做:
post_path(:slug => @post)
和
def default_url_options(options = {})
if options[:controller] == "posts" && options[:slug].is_a?Post
{
:year => options[:slug].created_at.year,
:month => options[:slug].created_at.month
}
else
{}
end
end
如果Post.to_param
返回了slug,这将有效。您只需要将年和月添加到哈希值。
答案 4 :(得分:1)
你可以节省压力并使用friendly_id。它真棒,做到了,你可以看看Ryan Bates的a screencast开始。