如何在Rails控制器中动态构建redirect_to的跳转路径
多嵌套路由下CRUD操作后优雅生成跳转路径(避免重复逻辑)
问题背景
在Rails应用中存在多层关联模型(如FinancialStatement、Budget、RevenueProjection),用户可在不同父级上下文(如FinancialStatement或Budget下)对RevenueProjection执行CRUD操作,路由配置了多级嵌套结构。需要将跳转路径逻辑抽象到ApplicationController中复用,避免在各控制器的update/create/destroy方法中编写大量if/else判断跳转地址。
关联模型定义
class FinancialStatement < ApplicationRecord belongs_to :company belongs_to :financial_year has_many :budgets has_many :revenue_projections, through: :budgets end class Budget < ApplicationRecord belongs_to :financial_statement belongs_to :division has_many :revenue_projections, dependent: :destroy end class RevenueProjection < ApplicationRecord belongs_to :potential_debtor, class_name: "Client", optional: true belongs_to :budget has_one :tenant, through: :budget has_one :division, through: :budget end
路由配置
resources :financial_statements do resources :revenue_projections end resources :budgets do resources :revenue_projections end # 支持三级嵌套的示例(如需) resources :financial_statements do resources :budgets do resources :revenue_projections end end
现有实现问题
已实现cud_redirect_path方法用于生成跳转路径,但存在参数传递错误:
- 直接传递
ancestors, object时,send方法会忽略最后一个参数 - 传递
ancestors + [object]会将URL中的/替换为. - 使用
*ancestors, object的方案仍未解决路径生成异常
解决方案:利用Rails内置多态路径方法
Rails提供的polymorphic_path可自动处理嵌套资源的路径生成,无需手动拼接路由辅助方法名,完美适配任意层级的嵌套场景。
修改后的cud_redirect_path方法
def cud_redirect_path(ancestors, object = nil) if object polymorphic_path(ancestors + [object]) else polymorphic_path(ancestors + [controller_name.pluralize.to_sym]) end end
方法说明
- 带对象的场景:将父级对象数组与目标对象合并,
polymorphic_path会自动匹配对应的嵌套路由辅助方法,例如:ancestors = [FinancialStatement.first]+object = RevenueProjection.first→ 生成/financial_statements/1/revenue_projections/1ancestors = [FinancialStatement.first, Budget.first]+object = RevenueProjection.first→ 生成/financial_statements/1/budgets/2/revenue_projections/3
- 不带对象的场景:将父级对象数组与控制器复数名称的符号合并,生成列表页路径,例如:
ancestors = [Budget.first]→ 生成/budgets/1/revenue_projections
自动加载父级对象(可选优化)
在ApplicationController中添加方法自动从请求参数中提取父级对象,支持任意层级嵌套:
def load_ancestors @ancestors = [] request.path_parameters.each do |key, value| # 排除当前资源的ID参数,仅处理父级资源ID next if key == "#{controller_name.singularize}_id" next unless key.end_with?('_id') model_class = key.gsub('_id', '').classify.constantize @ancestors << model_class.find(value) end end
在需要的控制器中通过before_action :load_ancestors调用,即可自动获取@ancestors数组。
验证效果
在RevenueProjectionsController的update方法中使用该方法,即可实现正确跳转:
def update respond_to do |format| if @revenue_projection.update(revenue_projection_params) format.html { redirect_to cud_redirect_path(@ancestors, @revenue_projection), notice: action_success_message } format.json { render json: { status: :ok, location: @revenue_projection }} else format.html { render :edit, status: :unprocessable_entity } format.json { render json: { errors: @revenue_projection.errors, status: :unprocessable_entity }} end end end
内容的提问来源于stack exchange,提问作者coby101
相关产品推荐
相关产品推荐

