如何在Ruby on Rails嵌套路由中使用多态关联
现有实现(顶层资源多态关联)
我采用的多态关联方案稳定可靠,适用于顶层资源,核心代码如下:
模型层
app/models/task.rb
class Task < ApplicationRecord belongs_to :taskable, polymorphic: true end
app/models/project.rb
class Project < ApplicationRecord has_many :phases has_many :tasks, as: :taskable end
控制器层
app/controllers/tasks_controller.rb
class TasksController < ApplicationController def new @task = @taskable.tasks.new end # 其他方法省略... end
app/controllers/projects/tasks_controller.rb
class Projects::TasksController < TasksController before_action :set_taskable private def set_taskable @taskable = Project.find(params[:project_id]) end end
路由配置
config/routes.rb
Rails.application.routes.draw do resources :projects do resources :phases resources :tasks, module: :projects end end
视图用法
app/views/tasks/show.html.erb
<%= link_to 'Edit task', edit_polymorphic_path([@taskable, @task]) %>
该方案能快速将任意顶层对象与Tasks关联,代码复用率高,无需使用concerns。
问题场景
现在需要让嵌套在Project下的Phase也通过Taskable与Tasks建立多态关联。尝试嵌套路由配置:
Rails.application.routes.draw do resources :projects do resources :phases do resources :tasks, module: :phases end resources :tasks, module: :projects end end
路由生成正常,但视图中多态路径需要传递3个参数:edit_polymorphic_path([@project, @taskable, @task]),破坏了原有视图的多态性——原来只需传[@taskable, @task],现在层级不同参数数量不一致,无法统一视图代码。
单独给Phases配置路由的尝试也失败:
Rails.application.routes.draw do resources :projects do resources :phases resources :tasks, module: :projects end resources :phases do resources :tasks, module: :phases end end
Rails不支持这种配置,因为未体现完整关联关系,无法生成正确路由。
解决方案
通过给嵌套模型添加to_ary方法,让多态路径自动识别嵌套父级,保持视图调用的一致性。
1. 修改Phase模型
在Phase模型中定义to_ary方法,返回包含父级Project的数组:
class Phase < ApplicationRecord belongs_to :project has_many :tasks, as: :taskable def to_ary [project, self] end end
2. 实现Phases::Tasks控制器
app/controllers/phases/tasks_controller.rb
class Phases::TasksController < TasksController before_action :set_taskable private def set_taskable @taskable = Phase.find(params[:phase_id]) end end
3. 保持嵌套路由配置
沿用之前的嵌套路由即可:
Rails.application.routes.draw do resources :projects do resources :phases do resources :tasks, module: :phases end resources :tasks, module: :projects end end
效果验证
现在无论@taskable是顶层的Project还是嵌套的Phase,视图中只需保持原有调用方式:
<%= link_to 'Edit task', edit_polymorphic_path([@taskable, @task]) %>
Rails会自动处理:
- 当
@taskable是Project时,生成edit_project_task_path(@taskable, @task) - 当
@taskable是Phase时,to_ary将其转换为[project, phase],最终生成edit_project_phase_task_path(@taskable.project, @taskable, @task)
扩展说明
如果后续有更深层级的嵌套资源,只需在对应模型中定义to_ary方法,返回包含所有父级对象的数组即可。若嵌套模型较多,也可使用concerns封装该逻辑,进一步减少代码重复。
内容的提问来源于stack exchange,提问作者Wayne Hiner

