我有许多模型可以在应用程序的多个区域进行编辑。我试图避免在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
这让我燃起了希望,但可悲的是,效果并没有任何不同。
2条答案
按热度按时间xurqigkl1#
所以,为了回答我自己的问题,方法定义是这样的:
注意需要在
if object
分支中单数化controller_name
,它已经为else
复数化了。sg24os4d2#
这里没有必要重新发明轮子。polymorphic route helpers已经为您处理了这一点。
举个疯狂的例子:
只要你的路由遵循Rails约定,你可以简单地传递一个包含所有记录的数组:
这将调用:
并得到路径
/blogs/1/categories/2/posts/3
。它通过使用
ActiveModel::Naming API
完成所有工作,该ActiveModel::Naming API
包含从类名派生路径助手名称的方法。如果你想重定向到索引而不是特定的记录,你可以这样做:
这将导致路径
/blogs/1/categories/2/posts
。