基于数据库模型的动态 Rails 路由 [英] Dynamic Rails routes based on database models

查看:18
本文介绍了基于数据库模型的动态 Rails 路由的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

So I'm building a Rails site that needs routes based on two different types

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

I have a Language model and a Category model

所以我需要能够去语言路线/ruby查看顶级ruby资源,也可以去/books查看所有语言的顶级书籍

So I need to be able to go to a language route /ruby to see top ruby resources and also go to /books to see top books in all languages

我尝试过这样的路线

get '/:language', to: "top_voted#language"
get '/:category', to: "top_voted#category"

问题在于逻辑无法弄清楚两者之间的区别,导致后端出现一些冲突

the problem with that was the logic could not figure out the difference between the two and caused some conflicts on the back end

我也试过这个

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 不允许路由中的数据库调用.有没有更简单的方法来做到这一点?我们需要能够以某种方式动态生成这些路由.

However the problem is Heroku where we are deploying this does not allow database calls in the routes. Is there an easier way to do this? We need to be able to dynamically generate these routes somehow.

推荐答案

使用路由约束有一个很好的解决方案.

There is a nice solution to that problem using routes constraints.

正如 rails 路由指南 所建议的,您可以在他们检查路径是否属于语言或类别的方式.

As the rails routing guide suggests, you could define routes constraints in a way that they check if a path belongs to a language or a category.

# 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? }

顺序定义了优先级.在上面的例子中,如果一种语言和一个类别具有相同的名称,则语言获胜,因为它的路由定义在类别路由之上.

The order defines the priority. In the above example, if a language and a category have the same name, the language wins as its route is defined above the category route.

如果你想确保所有路径都是唯一的,一个简单的方法是定义一个 Permalink 模型并在那里使用验证.

If you want to make sure, all paths are uniqe, an easy way would be to define a Permalink model and using a validation there.

生成数据库表:rails 生成模型 Permalink path:string reference_type:string reference_id:integer &&rails 数据库:迁移

并在模型中定义验证:

class Permalink < ApplicationRecord
  belongs_to :reference, polymorphic: true
  validates :path, presence: true, uniqueness: true

end

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

And associate it with the other object types:

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

end

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

This also allows you to define several permalink paths for a record.

rails_category.permalinks.create path: 'rails'
rails_category.permalinks.create path: 'ruby-on-rails'

使用此解决方案,路由文件必须如下所示:

With this solution, the routes file has to look like this:

# 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 之前通过永久链接加载记录:

And, as a side note for other users using the cancan gem and load_and_authorize_resource in the controller: You have to load the record by permalink before calling 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

这篇关于基于数据库模型的动态 Rails 路由的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆