You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

方法说明

  1. 带对象的场景:将父级对象数组与目标对象合并,polymorphic_path会自动匹配对应的嵌套路由辅助方法,例如:
    • ancestors = [FinancialStatement.first] + object = RevenueProjection.first → 生成/financial_statements/1/revenue_projections/1
    • ancestors = [FinancialStatement.first, Budget.first] + object = RevenueProjection.first → 生成/financial_statements/1/budgets/2/revenue_projections/3
  2. 不带对象的场景:将父级对象数组与控制器复数名称的符号合并,生成列表页路径,例如:
    • 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 22:02:50