自定义RESTful资源的url_for(复合键;不仅仅是id)

Paw*_*cki 9 ruby rest ruby-on-rails

给定以下资源定义:

map.resources :posts, :except => [:show]
map.post '/:year/:month/:slug, :controller => :posts, :action => :show
Run Code Online (Sandbox Code Playgroud)

我可以url_for使用以下语法为我工作:

<%= link_to @post.title, post_url(:year => '2010', :month => '02', :slug => 'test') %>
Run Code Online (Sandbox Code Playgroud)

但有没有办法让这项工作?

<%= link_to @post.title, @post %>
Run Code Online (Sandbox Code Playgroud)

目前它抛出此错误:

No route matches {:year=>#<Post id: 1, title: "test", (...)>, :controller=>"posts", :action=>"show"}
Run Code Online (Sandbox Code Playgroud)

显然它将@post对象传递给第一个路由参数(看起来像是一个Rails错误...).但我可以为我做这项工作吗?我要补充一点,搞乱default_url_options是一个死胡同.

仅在Rails 3.x中工作的解决方案是可以的,但我不想使用任何插件.

Luk*_*ncl 7

覆盖to_param.默认值为to_param记录ID,但您可以更改它.

class Post
  def to_param
    "/#{year}/#{month}/#{slug}"
  end
end
Run Code Online (Sandbox Code Playgroud)

(这假设您在Post类中有方法year,monthslug.)

现在,当生成URL时,to_param将调用被覆盖的内容.

<%= link_to @post.title, @post %> 
    => <a href="/2010/02/foobar">Foobar</a>
Run Code Online (Sandbox Code Playgroud)

显然,你必须确保你的PostsController show动作可以根据传递的参数找到记录.

您可能需要更改路径定义并消除此路线:

map.post '/:year/:month/:slug, :controller => :posts, :action => :show
Run Code Online (Sandbox Code Playgroud)

但是,通过to_param覆盖,默认RESTful post_path将适用于您要生成的URL.


ans*_*hul 6

如何固定url_forPostsController?可能不是很优雅,但它是干的,事情应该工作.

# app/controllers/posts_controller.rb 
class PostsController < ApplicationController

  protected

    def url_for(options = {} )
      if options[:year].class.to_s == "Post"
        obj = options[:year]
        options[:year] = obj.year
        options[:month] = obj.month
        options[:slug] = obj.slug
      end
      super(options)
    end

end
Run Code Online (Sandbox Code Playgroud)