在Rails的路由资源中更改:id参数的名称


107

我环顾了一下如何更改动态params插槽,并发现了此帖子确实有用。帖子是https://thoughtbot.com/blog/rails-patch-change-the-name-of-the-id-parameter-in

如果遵循以下路线,基本上就是这样做:

map.resources :clients, :key => :client_name do |client|
  client.resources :sites, :key => :name do |site|
    site.resources :articles, :key => :title
  end
end

这些路由创建以下路径:

/clients/:client_name
/clients/:client_name/sites/:name
/clients/:client_name/sites/:site_name/articles/:title

一种解决方案是重写def to_param模型中的方法,但我希望这样做而无需接触模型本身。

但是,由于它适用于Rails 2.x,我如何在Rails 3上实现相同的目的?

更新资料

这个程序正在使用Mongoid。不是AR。因此,宝石友好型不能用于afaik。

Answers:


192

Rails 4和5

在Rails 4中,:param添加了该选项,它似乎完全可以满足您的需求。您可以将Rails 3代码Rails 4代码进行比较

细节

您可以在routes.rb文件中轻松实现此目的:

# config/routes.rb

resources :posts, param: :slug

# app/controllers/posts_controller.rb

# ...
@post = Post.find_by(slug: params[:slug])
# ...

从Rails 4版本开始,此功能已在Rails指南中进行了说明

导轨3

不幸的是,在Rails 3中,的:key选项已resources被删除,因此您不能再通过传递额外的选项来轻松更改以这种方式创建的路线的名称。

细节

我想您已经在过去一年中以某种方式使应用程序按您想要的方式工作,但是我将探讨一种获得您在Rails 3中描述的效果的方法routes.rb。它将比该to_param方法涉及更多的工作。您还可以定义路由自定义参数使用定义scopematch(或它的表兄弟getputpost,和delete)。您只需在匹配器中输入所需的参数名称即可:

get 'clients/:client_name', :to => 'clients#show', :as => client

scope 'clients/:client_name' do
  get 'sites/:name', :to => 'sites#show', :as => site
end

您将必须手动添加resources为您自动创建的所有路由,但是它将实现您想要的。您还可以有效地将该:controller选项与scope和附加scope块一起使用,以消除某些重复。


编辑(2014年5月8日):使答案更加明显,其中包含有关Rails 3和4的信息。更新代码链接,以获取确切的行号和提交,以便它们可以工作更长的时间。

编辑(2014年11月16日): Rails 4应该位于顶部,并包括相关信息,因为它是Rails的当前版本已有相当一段时间了。

编辑(2016年8月9日):反映该解决方案仍在Rails 5中运行,并更新过时的链接。


1
因此,从根本上讲,Rails3在路由器中创建了resources选项,以使其保持完整状态,但为了拥有自定义变量名称,需要对其进行覆盖...感到la脚!
奥古斯丁·里丁格2013年

1
这是paramRails选项的备用
端口

这结束了URL辅助程序。如果我传入一个对象,或者抱怨传入的[:slug]键丢失,它们就会生成一条基于id的常规路由。有什么想法可以解决吗?
RonLugge 2014年

9
仅供参考,如果您使用嵌套资源,则父资源的参数将为post_slug,这可能会造成混淆。
ghayes

4
这确实很好,但是如果我不希望嵌套资源的父ID(例如post_post_id)的参数名怎么办?
Asnad Atta 2015年

45

在Rails 4中,通过param选项来更改:id参数。例如 resources :photos, param: :photo_name将生成/ photos /:photo_name


1

如果我对您的理解正确,那么您想要的是在URL中使用client_name而不是id,对吗?

您可以通过覆盖to_param模型中的方法来做到这一点。您可以在此处获得更多信息。


1
正如我已经提到的,我有不更改模型的约束。Rails 3是否不支持或不赞成上述示例?
Autodidact

1

有一个宝石,就像所有事物都有宝石;)

我一直在Rails 3中使用FriendlyId进行这种行为。

它将需要您向模型中添加一些代码,例如:

class Client < ActiveRecord::Base
  has_friendly_id :name
end

...并且如果您的客户没有URI兼容名称,则可能要为此使用slug,您可以使用has_friendly_id :name, :use_slug => true。当使用块时,您显然显然也需要将它们持久保存到数据库中。

就像已经提到的那样,您仍然可以将其to_param用于rails 3,如此处所述。我发现FriendlyId更灵活一些。


我喜欢友好的ID,并已在许多项目中使用。实际上我的应用程序使用的是Mongoid。而且friendly_id不支持它。正如我已经提到的,我的约束是不要更改模型。
Autodidact

1
如果您使用的是Mongoid,则可以使用Mongoid -slug来实现此功能。它实际上使用to_param,但是它在后台也执行其他一些巧妙的操作。我在这里找到链接:stackoverflow.com/questions/4744446/…–
Frost

我喜欢那颗宝石。还有另一种类似的人妖。但是正如我一直在说的,我确实有不修改模型的约束。这to_param打破了我的应用程序的前端部分。实际上,我拼命寻找路由方法,因为我必须更改API,但又不会破坏应用程序的Model和Frontend部分。为什么我不能只按照我所说的那样修改路线,以及上面旧版本的rails的示例帖子?
Autodidact

您是否尝试过重写控制器以使其使用自定义的路由键?例如@client = Client.find(:name => param[:client_name]ClientsController#show?那可能行得通。
弗罗斯特

Client.where(:name => param[:client_name]).first, 那是。
弗罗斯特,

0

在Rails 3中,您可以使用以下命名空间和范围的组合来重命名id密钥(虽然不是很好):

namespace :clients do
  scope "/:client_name" do
    namespace :sites do
      scope "/:name" do
         post "/:title" => "articles#create"
         ...
      end
    end
  end
end
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.