基于数据库模型的Dynamic Rails路线 [英] Dynamic Rails routes based on database models
问题描述
所以我正在构建一个需要基于两种不同类型的路线的Rails站点
我有一个语言模型和一个类别模型
所以我需要能够去语言路线/ ruby来查看顶级的ruby资源,并且还能够去/ books来查看所有语言的热门书籍
我尝试了这样的路线
get'/:language'到: top_voted#language
得到'/:category',以: top_voted#category
问题在于逻辑无法找出两者之间的差异,并在后端造成一些冲突
我也尝试过
Language.all.each都|语言|
获得#{language.name},以: top_voted#language,:language => language.name
结束
Category.all.each做| category |
获得#{category.name},以: top_voted#category,:category => category.name
结束
但是问题是Heroku在我们部署的地方不允许路由中的数据库调用。有没有更简单的方法可以做到这一点?我们需要能够以某种方式动态生成这些路由。
使用路由约束可以很好地解决该问题。 / p>
使用路线约束
作为铁路路线指南建议,您可以以检查路径是否属于某种语言或类别的方式定义路线约束。
#config / routes.rb
#...
得到':language',到:'top_voted#language',约束:lambda {|请求| Language.where(name:request [:language])。any? }
得到’:category’,到:‘top_voted#category’,约束:lambda {| request | Category.where(name:request [:category])。any? }
顺序定义优先级。在上面的示例中,如果一种语言和一种类别具有相同的名称,则该语言将获胜,因为其路径是在类别路径上方定义的。
使用永久链接模型
如果要确保所有路径都是唯一的,一种简单的方法是定义永久链接
模型并使用
生成数据库表: rails生成模型永久链接path:string reference_type:string reference_id:integer&& rails db:migrate
并在模型中定义验证:
类永久链接< ApplicationRecord
属居:引用,多态:true
验证:path,状态:true,唯一性:true
end
并将其与其他对象类型关联:
类语言< ApplicationRecord
has_many:permalinks,as::reference,dependent::destroy
end
这还允许您为一条记录定义几个永久链接路径。
rails_category.permalinks.create路径: 'rails'
rails_category.permalinks.create路径:'ruby-on-rails'
使用此解决方案,路由文件必须如下所示:
#config / routes.rb
#.. 。
获取':language',以:'top_voted#language',约束:lambda {| request | Permalink.where(引用类型:语言,路径:request [:language])。 }
得到’:category’,到:‘top_voted#category’,约束:lambda {| request | Permalink.where(reference_type:类别,路径:request [:category])。 }
此外,作为其他使用 cancan gem的用户的旁注和 load_and_authorize_resource
:在调用 load_and_authorize_resource
之前,必须通过永久链接加载记录:
class类别< ApplicationRecord
before_action:find_resource_by_permalink,仅::show
load_and_authorize_resource
private
def find_resource_by_permalink
@category || = Permalink.find_by( path:params [:category])。try(:reference)
结束
结束
So I'm building a Rails site that needs routes based on two different types
I have a Language model and a Category model
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
I tried routes like this
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
I also tried this
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
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.
Using routes constraints
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.
Using a Permalink model
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.
Generate the database table: rails generate model Permalink path:string reference_type:string reference_id:integer && rails db:migrate
And define the validation in the model:
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? }
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
这篇关于基于数据库模型的Dynamic Rails路线的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!