问题描述
所以我正在构建一个需要基于两种不同类型的路由的 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 路由的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!