动态Rails基于数据库模型的路由

Joe*_*ith 6 ruby routes ruby-on-rails heroku

所以我正在构建一个需要基于两种不同类型的路由的Rails站点

我有一个语言模型和一个分类模型

因此,我需要能够使用语言路线/红宝石查看顶级红宝石资源,还可以访问/书籍查看所有语言的热门书籍

我试过这样的路线

get '/:language', to: "top_voted#language"
get '/:category', to: "top_voted#category"
Run Code Online (Sandbox Code Playgroud)

问题是逻辑无法弄清楚两者之间的差异,并在后端引起了一些冲突

我也尝试过这个

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
Run Code Online (Sandbox Code Playgroud)

但问题是我们正在部署它的Heroku不允许在路由中进行数据库调用.有更简单的方法吗?我们需要能够以某种方式动态生成这些路由.

fie*_*edl 8

使用路径约束可以很好地解决这个问题.

使用路线约束

正如rails路由指南所建议的那样,您可以通过检查路径是否属于某种语言或类别来定义路径约束.

# 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? }
Run Code Online (Sandbox Code Playgroud)

订单定义优先级.在上面的示例中,如果语言和类别具有相同的名称,则语言将获胜,因为其路由定义在类别路由之上.

使用永久链接模型

如果您想确保所有路径都是独一无二的,那么一种简单的方法就是定义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
Run Code Online (Sandbox Code Playgroud)

并将其与其他对象类型相关联:

class Language < ApplicationRecord
  has_many :permalinks, as: :reference, dependent: :destroy

end
Run Code Online (Sandbox Code Playgroud)

这也允许您为记录定义几个永久链接路径.

rails_category.permalinks.create path: 'rails'
rails_category.permalinks.create path: 'ruby-on-rails'
Run Code Online (Sandbox Code Playgroud)

使用此解决方案,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? }
Run Code Online (Sandbox Code Playgroud)

并且,作为使用cancan gem和load_and_authorize_resource控制器的其他用户的旁注:您必须在调用之前通过永久加载来加载记录load_and_authorize_resource:

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
Run Code Online (Sandbox Code Playgroud)