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

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

问题描述

所以我正在构建一个需要基于两种不同类型的路线的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屋!

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