Rails控制器#edit操作中嵌套资源的条件路由,具体取决于来自

时间:2015-08-08 22:11:32

标签: ruby-on-rails redirect controller routing

我有一个has_many条的Foo资源。我使用嵌套资源进行有限数量的操作,但是我更喜欢保持我对条形图的路由较浅。有两种方法可以导航到Bar对象的编辑视图 - 从包含foo的嵌套路径,或从不在{{1}内嵌套的较浅bar路径导航}。例如,用户可能会点击foo页面上的修改按钮;或者来自/foos/[:foo_id]/bar/[:bar_id]

在第一种情况下,我希望控制器在更新记录后将用户重定向回父foo页面:/bars/[:bar_id]。在第二种情况下,我希望它重定向到/foos/[:foo_id]bars的索引视图。我相信我需要在/bars控制器中的#edit操作中使用某种条件,该操作将告诉Rails执行bars后的位置。

#update

# config/routes.rb resources :foos do resources :bars, only: [:new, :edit] end resources :bars # bin/rake routes: foo_bars POST /foos/:foo_id/bars(.:format) bars#create new_foo_bar GET /foos/:foo_id/bars/new(.:format) bars#new edit_foo_bar GET /foos/:foo_id/bars/:id/edit(.:format) bars#edit bars GET /bars(.:format) bars#index POST /bars(.:format) bars#create new_bar GET /bars/new(.:format) bars#new edit_bar GET /bars/:id/edit(.:format) bars#edit bar GET /bars/:id(.:format) bars#show PATCH /bars/:id(.:format) bars#update PUT /bars/:id(.:format) bars#update DELETE /bars/:id(.:format) bars#destroy 的控制器:

bars

我尝试更改# app/controllers/bar_controller.rb def edit @bar = bar.find(params[:id]) @foo = @bar.foo end def update @bar = bar.find(params[:id]) @foo = @bar.foo respond_to do |format| if @bar.update_attributes(bar_params) format.html { redirect_to @foo, notice: "bar successfully updated" } else format.html { render action: "edit" } end end end 操作中的redirect_to @foo行,因此有条件逻辑可以为#update切换@foo,具体取决于@bars {1}}已采取行动。我尝试过以下内容来测试调用#edit操作时是否存在params[:foo],为重定向设置实例变量。

#edit

这不起作用。 Rails陈述def edit if params[:foo] @redirect_page = @foo else @redirect_page = @bars end @bar = bar.find(params[:id]) @foo = @bar.foo end def update # code omitted... format.html { redirect_to @redirect_page, notice: "bar successfully updated" } # code omitted... end 。我还在cannot redirect to nil!操作中使用基于URI(request.referer).path的测试尝试了一些操作,但没有成功。

我还不完全清楚Rails魔法是如何在控制器中发生的。我相信#edit操作是定义重定向的条件(或通过#edit操作中调用的方法)的适当位置,因为控制器将在其中执行操作。见"传入的请求并知道它来自何处。但我无法理解捕获这些信息,并将其传递给#edit。感谢任何指导。

2 个答案:

答案 0 :(得分:2)

在您的修改表单中,添加hidden_field_tag

<%= hidden_field_tag "route", request.env['PATH_INFO'] %>

然后在您的控制器中,您可以拥有if语句,并根据redirect_to的内容使用params[:route]

答案 1 :(得分:0)

我明白了。使用request.env['PATH_INFO]的params [:route]方法对我来说并不适用,因为&#39; PATH_INFO&#39;表单中的变量提供了bars#update操作的路径,而不是启动bars#edit操作的路径。

点击&#34;编辑&#34;来自/foos/[:id]的父foo页面,params散列是:

>>  params
=> {"controller"=>"bars", "action"=>"edit", "foo_id"=>"3786", "id"=>"16"}

首次访问表单时params[:route]没有值 - 隐藏字段仅在点击&#34后更新到params哈希值;更新&#34;在编辑表格中:

>>  params[:route]
=> "/foos/3786/bars/16/edit"

这可行,但需要构建逻辑来解析路由才能重定向到/foos/[:foo_id]

事实证明,使用Rails flash方法存储重定向到源页面的路径更为简单。我通过在BarsController中调用自定义方法set_redirect_path并在bars#edit中调用它来完成此操作。这将为闪存中的源设置一个值,该值在bars#update中可用。也许有更好/更传统的方式来实现这一目标,但这似乎是一种干净而简单的方式来做我想要的。

# app/controllers/bars_controller.rb
def edit
  set_redirect_path
  @bar = bar.find(params[:id])
  @foo = @bar.foo
end

def update
  @bar = bar.find(params[:id])
  @foo = @bar.foo

  respond_to do |format|
    if @bar.update_attributes(bar_params)
      format.html { redirect_to flash[:source], notice: "bar successfully updated" }
      format.xml { head :ok }
    else
      format.html { render action: "edit" }
      format.xml { render xml: @bar.errors, status: :unprocessable_entity }
    end
  end
end

private
  def set_redirect_path
    flash[:source] = URI(request.referer).path
  end

这种方法的一个优点是我现在可以摆脱共享部分app/views/bars/_list.html.haml中的条件逻辑,这是确定是否单击&#34;编辑&#34;按钮应该路由到edit_foo_bar_pathedit_bar_path(即如果存在@foo则选择前者)。因此,我可以删除嵌套资源:edit的{​​{1}}。由于Flash捕获请求的传入源并将其存储在:bars操作中以供参考,因此所有编辑请求都可以使用相同的#update,无论它们来自何处。更新后,Rails会将用户重定向到他们发起edit_bar_path操作的位置。