所以我正在构建一个需要基于两种不同类型的路由的Rails站点
我有一个语言模型和一个分类模型
因此,我需要能够使用语言路线/红宝石查看顶级红宝石资源,还可以访问/书籍查看所有语言的热门书籍
我试过这样的路线
get '/:language', to: "top_voted#language"
get '/:category', to: "top_voted#category"
问题是逻辑无法弄清楚两者之间的差异并在后端引起一些冲突
我也试过这个
Language.all.each do |language|
get "#{language.name}", to: "top_voted#language", :language => language.name
end
Category.all.each do |category|
get "#{category.name}", to: "top_voted#category", :category => category.name
end
然而问题是我们正在部署它的Heroku不允许路由中的数据库调用。有更简单的方法吗?我们需要能够以某种方式动态生成这些路线。
答案 0 :(得分:7)
使用路径约束可以很好地解决这个问题。
正如rails routing guide建议的那样,您可以通过检查路径是否属于某种语言或类别来定义路径约束。
# config/routes.rb
# ...
get ':language', to: 'top_voted#language', constraints: lambda { |request| Language.where(name: request[:language]).any? }
get ':category', to: 'top_voted#category', constraints: lambda { |request| Category.where(name: request[:category]).any? }
订单定义优先级。在上面的示例中,如果语言和类别具有相同的名称,则语言将获胜,因为其路由定义在类别路由之上。
如果您想确保所有路径都是独一无二的,一种简单的方法是定义Permalink
模型并在那里使用验证。
生成数据库表:rails generate model Permalink path:string reference_type:string reference_id:integer && rails db:migrate
并在模型中定义验证:
class Permalink < ApplicationRecord
belongs_to :reference, polymorphic: true
validates :path, presence: true, uniqueness: true
end
并将其与其他对象类型相关联:
class Language < ApplicationRecord
has_many :permalinks, as: :reference, dependent: :destroy
end
这也允许您为记录定义几个永久链接路径。
rails_category.permalinks.create path: 'rails'
rails_category.permalinks.create path: 'ruby-on-rails'
使用此解决方案,routes文件必须如下所示:
# config/routes.rb
# ...
get ':language', to: 'top_voted#language', constraints: lambda { |request| Permalink.where(reference_type: 'Language', path: request[:language]).any? }
get ':category', to: 'top_voted#category', constraints: lambda { |request| Permalink.where(reference_type: 'Category', path: request[:category]).any? }
并且,作为在控制器中使用 cancan gem和load_and_authorize_resource
的其他用户的旁注:您必须在调用load_and_authorize_resource
之前通过永久加载来加载记录:< / p>
class Category < ApplicationRecord
before_action :find_resource_by_permalink, only: :show
load_and_authorize_resource
private
def find_resource_by_permalink
@category ||= Permalink.find_by(path: params[:category]).try(:reference)
end
end
答案 1 :(得分:1)
这听起来像是一个架构问题。如果干净的网址对您很重要,请按以下步骤进行设置:
创建一个名为Page
的新模型,该模型属于特定资源(类别或语言)。
class Page < ActiveRecord::Base
belongs_to :resource, polymorphic: true
end
数据库列将是id
,resource_type
,resource_id
,path
,以及您想要挂在那里的任何其他内容。
你的其他模特会有互惠的关系:
has_many :pages, as: :resource
现在您可以使用单个路径进行路由,但仍然可以访问来自不同类的资源。
路由器:
resources :pages, id: /[0-9a-z]/
控制器:
class PagesController
def show
@page = Page.find_by_path(params[:id])
end
end
在视图中,为资源模型设置部分,然后在pages/show
中进行渲染:
=render @page.resource
示例页面为#<Page path: 'ruby', resource: #<Language name: "Ruby">>
,可在/pages/ruby
处获取。您可以将/ruby
路由路由到PagesController
,但是您严格限制了可以在应用中其他位置使用的路由数量。
答案 2 :(得分:1)
由于我已经迟到了几个月,你可能已经想出了一些东西,但对于未来的人来说,限制可能就是你正在寻找的东西。您可以设置基于请求对象决定的lambda,也可以设置一个实现matches?
方法的类,以便路由器调用。
http://guides.rubyonrails.org/routing.html#advanced-constraints
答案 3 :(得分:0)
两个路由得到'/:language'和得到'/:category'对于rails来说完全相同。
Rails路由器无法区分/books
和/ruby
。
在这两种情况下,rails只会在routes.rb
中查找类似于/something
的路由,它将选择第一个匹配并将路由分派到指定的控制器操作。
在你的情况下,
所有/something
格式的请求
将匹配
get '/:language', to: "top_voted#language"