ruby-on-rails 如何在Rails控制器中动态构建redirect_to的路径

2g32fytz  于 2023-04-08  发布在  Ruby
关注(0)|答案(2)|浏览(125)

我有许多模型可以在应用程序的多个区域进行编辑。我试图避免在update/create/destroy控制器方法中使用大量if/else语句来确定正确的“成功”重定向路径。我还希望我可以将其抽象到足以将其放入application_controller中并在任何需要的地方使用它。
作为一个简单的例子,我有这个ER模型:

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

用户可以在FinancialStatement或Budget下对RevenueProjection对象执行部分/全部CRUD操作,具体取决于用例和访问级别等。(我甚至希望在财务报表下的预算下也执行相同的操作)
以下是路线的相关部分:

resources :financial_statements do
    resources :revenue_projections do
    end
  end

  resources :budgets do
    resources :revenue_projections do
    end
  end

所以我有一个一半的解决方案,它基本上可以工作,并且可以更清楚地显示我希望发生的事情。下面是revenue_projections_controller.rb中的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

@ancestors是从URL参数中设置的父对象的数组,在本例中只有一代深度,但我确实有多达3层嵌套的用例(故意但希望不会误导关于嵌套路由的传统智慧)。我对cud_redirect_path方法签名很满意,它的定义如下所示。

def cud_redirect_path(ancestors, object = nil)
    path_extension = ancestors.blank? ? '' : (ancestors.map { |anc| anc.class.name.underscore }.join('_') + '_')
    if object
      send("#{path_extension}#{controller_name}_path", ancestors, object)
    else
      send("#{path_extension}#{controller_name.pluralize}_path", ancestors)
    end
  end

if object分支没有做我想做的,基本上忽略了最后一个要发送的参数。我试着传递(ancestors + [object]),它很接近,但在URL中放了“.”而不是“/”。我使它工作的唯一方法是通过手动逐元素布局数组元素,这意味着根据祖先数组的长度进行分支,违反了DRY原则。我试图找到一个等效的运算符,它可以在数组参数上工作,但不能。如果这是Common Lisp,我会使用#'apply。如果Rails,它所有的路径查找魔术,不能提供一个很好的方式来接受一个可变长度的父列表加上一个对象,并返回正确的路径,我会感到惊讶,它还没有让我失望!我有一个唠叨的感觉,我已经看到了什么地方太...
先谢谢你了!
[更新] ChatGPT给了我这个:

def cud_redirect_path(ancestors, object = nil)
  path_extension = ancestors.blank? ? '' : (ancestors.map { |anc| anc.class.name.underscore }.join('_') + '_')
  if object
    send("#{path_extension}#{controller_name}_path", *ancestors, object)
  else
    send("#{path_extension}#{controller_name.pluralize}_path", *ancestors)
  end
end

这让我燃起了希望,但可悲的是,效果并没有任何不同。

xurqigkl

xurqigkl1#

所以,为了回答我自己的问题,方法定义是这样的:

def cud_redirect_path(ancestors, object = nil)
    path_extension = ancestors.blank? ? '' : (ancestors.map { |anc| anc.class.name.underscore }.join('_') + '_')
    if object
      send("#{path_extension}#{controller_name.singularize}_path", *ancestors, object)
    else
      send("#{path_extension}#{controller_name}_path", *ancestors)
    end
  end

注意需要在if object分支中单数化controller_name,它已经为else复数化了。

sg24os4d

sg24os4d2#

这里没有必要重新发明轮子。polymorphic route helpers已经为您处理了这一点。
举个疯狂的例子:

resources :blogs do
  resources :categories do
    resources :posts # Much nesting, such wow!
  end 
end
@ancestors = [Blog.find(1), Category.find(2)]
@record = Post.find(3)

只要你的路由遵循Rails约定,你可以简单地传递一个包含所有记录的数组:

redirect_to [*@ancestors, @record]
# shorthand for 
redirect_to(
  polymophic_path([Blog.find(1), Category.find(2), Post.find(3)]
)

这将调用:

blog_category_post_path(blog_id: 1, category_id: 2, id: 3)

并得到路径/blogs/1/categories/2/posts/3
它通过使用ActiveModel::Naming API完成所有工作,该ActiveModel::Naming API包含从类名派生路径助手名称的方法。
如果你想重定向到索引而不是特定的记录,你可以这样做:

redirect_to [*@ancestors, @record.class]

这将导致路径/blogs/1/categories/2/posts

相关问题