如何在相同资源的不同路由上使用不同的路由ID?

时间:2017-03-22 10:26:40

标签: ruby-on-rails ruby routes

我从来没有找到解决这个问题的好方法。我有以下路线结构:

resources :contents

namespace :admin do
  resources :contents
end

当我呼叫content_path(content)时,我希望id成为内容的slug,而当我呼叫admin_content_path(content)时,我希望id成为内容的id。我只是希望id不与模型相关(实际上id是模型的to_param方法的返回值),而是与路径相关。

我想避免为每条路线定义辅助方法,在我看来这是一个微弱的解决方案。

我知道我可以写admin_content_path(id: content.id)content_path(id: content.slug),但实际上这只是一个黑客攻击。此外,这在form_for中尤其令人讨厌,因为我无法写

form_for @content

但我被迫使用

form_for @content, url: @content.new_record? ? admin_contents_path : admin_contents_path(id: @content.id)

3 个答案:

答案 0 :(得分:1)

通常,您会将路线更改为:

resources :contents, param: :slug

然后覆盖to_param方法成为:

class Content < ApplicationRecord
  def to_param
    slug
  end
end

最后在您的控制器中,您将Content.find(params[:id]替换为Content.find_by(slug: params[:slug])

当您拨打/contents/foo-bar时,这会为您提供content_path(content)这样的网址。

在您的情况下,您还可以创建一个覆盖to_param方法的子类:

module Admin
  class Content < ::Content
    def to_param
      id && id.to_s # This is the default for ActiveRecord
    end
  end
end

由于您的admin/contents_controller.rbAdmin下被命名空间(例如Admin::ContentsController),默认情况下会使用Admin::Content类而不是普通的Content类,因此,对象本身和所有路径应该如你所愿,包括表格。

答案 1 :(得分:1)

我想说的是两个不同的问题:用户前端的资源生成URL(使用slugs)和管理表单的URL生成。

显然,在您的管理员中,您永远无法编写form_for @resource,因为您的管理员已命名,因此最低限度至少为form_for [:admin, @resource]

假设您在某些模型上有to_param来返回slug,您可以在管理员后台创建自己的自定义帮助程序,以便始终返回名为{{1}的命名空间的路径并使用记录的/admin/

这样做的一种通用方法是在id根控制器中添加此类代码。

Admin

然后在您的表单中,您可以使用class Admin::AdminController < ApplicationController helper_method :admin_resource_path, :edit_admin_resource_path def admin_resource_path(resource) if resource.new_record? polymorphic_path([:admin, ActiveModel::Naming.route_key(resource)]) else polymorphic_path([:admin, ActiveModel::Naming.singular_route_key(resource)], id: resource.id) end end def edit_admin_resource_path(resource) polymorphic_path([:edit, :admin, ActiveModel::Naming.singular_route_key(resource)], id: resource.id) end end 。它适用于用户创建和用户编辑。 您也可以在控制器中使用这些帮助程序来重定向...

答案 2 :(得分:0)

好吧,我找到了一个不错的解决方案,但只在Rails&gt; = 5.1(目前在rc1中),使用全新的direct method

namespace :admin do
  resources :contents
end

# Maps admin content paths in order to use model.id instead of model.to_param
{ admin_content: :show, edit_admin_content: :edit }.each do |direct_name, action|
  direct direct_name do |model, options|
    options.merge(controller: 'admin/contents', action: action, id: model.id)
  end
end